UUIDs: La Guía Definitiva para IDs Distribuidos sin Dolor de Cabeza
Descubre por qué los IDs secuenciales fallan en sistemas modernos, cómo funcionan realmente los UUIDs (v4, v7), y cuándo usar cada tipo sin sacrificar rendimiento.
El día que mi sistema dejó de funcionar: la historia de los IDs secuenciales
Estaba desarrollando una app de comercio electrónico. Todo iba bien hasta que el cliente quiso "una pequeña modificación": dos servidores en lugar de uno para alta disponibilidad. "Simple", pensé. "Solo configuro un load balancer."
El problema apareció a las 3 AM del domingo siguiente. Dos pedidos diferentes, procesados simultáneamente en servidores diferentes, recibieron el mismo ID: 15432. El caos que siguió me llevó a entender por qué los IDs secuenciales, tan simples y elegantes, son la elección equivocada para sistemas modernos.
Ese día aprendí sobre UUIDs. No porque quisiera, sino porque necesitaba una solución donde cada parte del sistema pudiera generar IDs sin preguntar a nadie más, y sin riesgo de colisiones.
IDs Secuenciales: perfectos hasta que no lo son
Los IDs auto-incrementales (1, 2, 3...) son la forma más simple de identificar registros:
CREATE TABLE usuarios (
id SERIAL PRIMARY KEY,
nombre VARCHAR(255)
);
// El primer usuario tiene id: 1
// El segundo: id: 2
// Y así sucesivamente...
Ventajas irresistibles
- Compactos: 4 bytes (INTEGER) o 8 bytes (BIGINT)
- Legibles:
/usuario/42es intuitivo - Ordenables cronológicamente: ID 100 se creó antes que ID 200
- Rendimiento de índices excepcional: Los B-trees aman las inserciones secuenciales
- Mentalmente simple: No necesitas explicar qué es un ID a nuevos desarrolladores
El problema que nadie ve hasta que es tarde
Los IDs secuenciales requieren coordinación centralizada. Para saber cuál es el "siguiente" ID, necesitas preguntar a la base de datos maestra.
En una arquitectura moderna esto crea cuellos de botella:
- Múltiples servidores: ¿Cuál genera los IDs? ¿Replicas de solo lectura?
- Microservicios: ¿Cada uno tiene su propia secuencia? ¿Cómo evitas colisiones?
- Offline-first: ¿Cómo genera IDs el cliente sin conexión?
- Sharding: ¿Cómo divides los IDs entre múltiples bases de datos?
La solución: identificadores que sean únicos por diseño, sin necesidad de coordinación.
UUIDs: el concepto de "único global"
Un UUID (Universally Unique Identifier) es un número de 128 bits (16 bytes) diseñado para ser único en todo el universo, sin necesidad de coordinación central.
En su representación canónica:
550e8400-e29b-41d4-a716-446655440000
|______| |_| |____| |____________|
(8) (4) (4) (12)
Son 32 caracteres hexadecimales agrupados como 8-4-4-4-12, separados por guiones.
La promesa: La probabilidad de que dos UUIDs generados independientemente sean idénticos es tan baja que puedes asumir que nunca ocurrirá. Es como la promesa de que dos personas nunca tendrán la misma huella dactilar.
Genera UUIDs instantáneamente con nuestra herramienta UUID Generator.
Las versiones de UUID: no todos son iguales
UUID es una familia de algoritmos, no uno solo. La IETF ha definido múltiples versiones para diferentes casos de uso:
UUID v1: Timestamp + MAC address (el olvidado)
Basado en la hora actual y la dirección MAC de la máquina:
- Ventaja: Ordenable cronológicamente
- Desventaja: Revela información (hora de creación + identificador de hardware)
- Estado: Prácticamente obsoleto por preocupaciones de privacidad
UUID v4: Aleatorio puro (el estándar actual)
122 bits aleatorios criptográficamente seguros:
// Navegador moderno
const uuid = crypto.randomUUID();
console.log(uuid); // "f47ac10b-58cc-4372-a567-0e02b2c3d479"
// Node.js
const { randomUUID } = require('crypto');
const uuid = randomUUID();
¿Por qué es tan seguro contra colisiones?
Con 122 bits aleatorios, tienes 2^122 combinaciones posibles (5.3 × 10^36). Para ponerlo en perspectiva:
- Si generas 1 billón de UUIDs por segundo durante 100 años, la probabilidad de una colisión sigue siendo menor que ganar la lotería 3 veces seguidas.
- Hay más UUIDs posibles que átomos en todos los humanos de la Tierra.
Desventaja clave: Los UUIDs v4 son completamente aleatorios. Esto significa que no son ordenables cronológicamente, lo cual tiene implicaciones de rendimiento importantes (veremos esto más adelante).
UUID v7: El futuro (timestamp + aleatorio)
Combina lo mejor de v1 (orden cronológico) con la privacidad de v4:
018e1e2a-1234-7xxx-xxxx-xxxxxxxxxxxx
^^^^^^^
timestamp Unix en milisegundos (48 bits)
Los primeros 48 bits son un timestamp, los siguientes son aleatorios. Esto significa que:
- Son ordenables cronológicamente (útil para índices)
- No revelan información sensible
- Aún tienen suficiente aleatoriedad para unicidad
Problema: Aún no está ampliamente soportado nativamente. Necesitas librerías como uuid v9+:
import { v7 as uuidv7 } from 'uuid';
const uuid = uuidv7();
// "018e2b8c-3e1b-7b3a-9c2d-7a6b5c4d3e2f"
Generando UUIDs: de lo nativo a lo compatible
Opción 1: Web Crypto API (recomendada, moderna)
// Disponible en navegadores modernos y Node.js 14.17+
const uuid = crypto.randomUUID();
console.log(uuid); // "f47ac10b-58cc-4372-a567-0e02b2c3d479"
Ventajas: Nativo, rápido, usa CSPRNG del sistema operativo.
Opción 2: Librería uuid (universal)
npm install uuid
import { v4, v7 } from 'uuid';
// UUID v4 (aleatorio)
const id = v4(); // "f47ac10b-58cc-4372-a567-0e02b2c3d479"
// UUID v7 (timestamp-based, si instalas versión 9+)
const idOrdenable = v7(); // "018e2b8c-3e1b-7b3a-9c2d-7a6b5c4d3e2f"
Opción 3: Polyfill para navegadores antiguos
// Implementación simple de UUID v4
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// ⚠️ Advertencia: Usa Math.random(), no es criptográficamente seguro
// Solo para casos donde la unicidad "razonable" es suficiente
El impacto en rendimiento: UUIDs vs Secuenciales
Aquí está la parte que muchos tutoriales omiten: los UUIDs tienen implicaciones de rendimiento reales.
Por qué los IDs secuenciales son tan rápidos
Los índices B-tree (usados por PRIMARY KEY en PostgreSQL, MySQL, etc.) funcionan mejor con inserciones secuenciales:
B-tree con IDs secuenciales (1, 2, 3, 4, 5...):
[3]
/ [1,2] [4,5]
Las inserciones siempre van al final derecho. Mínima reorganización.
El problema con UUIDs v4 aleatorios
Los UUIDs v4 se distribuyen aleatoriamente por todo el espacio de 128 bits:
B-tree con UUIDs aleatorios:
[uuid_A]
/ [uuid_X] [uuid_B, uuid_C]
El próximo UUID puede ir a CUALQUIER parte del árbol.
Constante reorganización = más I/O = más lento.
Impacto medible:
- Inserciones pueden ser 3-10x más lentas con UUIDs v4 vs secuenciales
- Índices más fragmentados (más espacio en disco)
- Menos eficiente para cache de consultas
La solución: UUIDs ordenables (v7) o ULID
UUID v7 soluciona esto al incluir un timestamp al inicio:
UUID v7 ordenados cronológicamente:
018e2b8c-xxx (hoy, 10:00)
018e2b8d-xxx (hoy, 10:01)
018e2b8e-xxx (hoy, 10:02)
Los bits más significados cambian lentamente,
manteniendo localidad temporal similar a IDs secuenciales.
Alternativa: ULID (Universally Unique Lexicographically Sortable Identifier) es similar a UUID v7 pero con 26 caracteres en lugar de 36.
Cuándo usar UUIDs (y cuándo evitarlos)
✅ Casos donde UUIDs brillan
1. Sistemas distribuidos / Microservicios
Cuando tienes múltiples servicios generando datos:
// Servicio de Pedidos (servidor A)
const pedido = {
id: crypto.randomUUID(), // Genera sin preguntar a nadie
items: [...],
total: 99.99
};
// Servicio de Inventario (servidor B)
const movimiento = {
id: crypto.randomUUID(),
productoId: 'abc',
cantidad: -1
};
// Sin coordinación, sin riesgo de colisión
2. Aplicaciones Offline-First
Cuando el cliente puede crear datos sin conexión:
// App móvil sin conexión
const nuevaNota = {
id: crypto.randomUUID(), // Generado localmente
titulo: 'Ideas de proyecto',
contenido: '...',
syncStatus: 'pending' // Se sincronizará cuando haya WiFi
};
// Guardar en IndexedDB
await db.notas.add(nuevaNota);
3. Seguridad por oscuridad (no revelar volumen)
Con IDs secuenciales, /factura/123 revela que existen al menos 123 facturas. Con UUIDs:
/factura/f47ac10b-58cc-4372-a567-0e02b2c3d479
Ninguna información sobre el volumen total.
4. Merge de datos de múltiples fuentes
Importar datos de sistemas externos sin conflictos:
// Datos de adquisición de otra empresa
const usuariosImportados = usuariosExternos.map(u => ({
...u,
id: crypto.randomUUID(), // Nuevos IDs, cero conflictos
source: 'legacy_system'
}));
❌ Casos donde los IDs secuenciales siguen siendo mejores
1. Tablas pequeñas con muchos JOINs
UUIDs son 4x más grandes que INTEGER (16 bytes vs 4 bytes). En tablas con millones de registros y muchos JOINs, esto afecta:
- Uso de memoria
- Tamaño de índices
- Velocidad de JOINs
2. URLs que los humanos escriben manualmente
✅ Bueno: /admin/user/42/edit
❌ Malo: /admin/user/f47ac10b-58cc-4372-a567-0e02b2c3d479/edit
Para URLs públicas amigables, considera usar slugs (usuario/juan-garcia) o IDs secuenciales alternativos.
3. Sistemas donde el orden cronológico es crítico y no puedes usar UUID v7
Si tu aplicación depende de ordenar por ID para obtener orden de creación (patrón antipatrón pero común), UUIDs v4 romperán esto.
Migrando de IDs secuenciales a UUIDs
No cambies todos los IDs de golpe. Estrategia gradual:
Paso 1: Añadir UUID como columna adicional
-- Añadir UUID sin romper nada existente
ALTER TABLE usuarios ADD COLUMN uuid UUID DEFAULT gen_random_uuid();
CREATE UNIQUE INDEX idx_usuarios_uuid ON usuarios(uuid);
-- Los registros existentes obtienen UUID automáticamente
-- Los nuevos pueden usar uuid o id secuencial
Paso 2: Actualizar código gradualmente
// Versión 1: API devuelve ambos IDs
{
"id": 15432, // Legacy
"uuid": "f47ac10b-58cc-4372-a567-0e02b2c3d479", // Nuevo
"nombre": "Ana"
}
// Versión 2: Nuevos endpoints usan solo UUID
// GET /api/v2/usuarios/f47ac10b-58cc-4372-a567-0e02b2c3d479
// Versión 3: Deprecar IDs secuenciales en API pública
// Mantener internamente para JOINs existentes
Paso 3: (Opcional) Migrar completamente
Solo si realmente necesitas eliminar los IDs secuenciales:
-- Advertencia: Rompe todas las foreign keys
BEGIN;
-- Crear nuevas tablas con UUID como PK
-- Migrar datos
-- Actualizar todas las foreign keys
-- Eliminar tablas viejas
COMMIT;
La mayoría de sistemas nunca llegan al paso 3. Tener ambos tipos de IDs (secuencial interno, UUID externo) es un patrón completamente válido.
Errores comunes con UUIDs
Error 1: Tratar UUIDs como strings de 36 caracteres
Algunos desarrolladores almacenan UUIDs como VARCHAR(36) en lugar del tipo nativo UUID:
-- ❌ Mal: Más espacio, sin validación de formato
CREATE TABLE usuarios (
id VARCHAR(36) PRIMARY KEY -- 36 bytes + overhead
);
-- ✅ Bien: 16 bytes exactos, validación automática
CREATE TABLE usuarios (
id UUID PRIMARY KEY -- 16 bytes
);
Error 2: Indexar UUIDs v4 en campos de alta cardinalidad sin considerar fragmentación
Los UUIDs v4 aleatorios causan fragmentación de índices. Si tienes tablas masivas (>10M filas) con inserciones frecuentes:
-- Considera UUID v7 o ULID en lugar de v4
-- O acepta el trade-off de mantenimiento de índices
REINDEX TABLE usuarios; -- PostgreSQL
OPTIMIZE TABLE usuarios; -- MySQL
Error 3: Exponer UUIDs en logs sin necesidad
Aunque UUIDs no revelan información de secuencia, sigue siendo información de identificación. En logs públicos, considera hashear o anonimizar:
// ❌ Log detallado
console.log(`Usuario f47ac10b-58cc-4372-a567-0e02b2c3d479 hizo login`);
// ✅ Log anonimizado
console.log(`Usuario {uuid_redacted} hizo login`);
Conclusión: la elección correcta para tu contexto
No hay "mejor" tipo de ID universal. La elección depende de tu arquitectura:
| Escenario | Recomendación | Razón |
|---|---|---|
| Monolito simple, un servidor | IDs secuenciales | Simplicidad, rendimiento |
| Microservicios | UUID v4 o v7 | Sin coordinación necesaria |
| Offline-first / PWA | UUID v4 | Generación cliente segura |
| Alta escritura, índices grandes | UUID v7 o ULID | Localidad temporal |
| APIs públicas | UUID v4 | No revelar volumen |
| URLs amigables | Slugs + IDs secuenciales | UX para humanos |
La buena noticia: puedes migrar gradualmente. Empieza con UUIDs para nuevas tablas o como columna adicional, y evoluciona según necesites.
Genera UUIDs para tus proyectos usando nuestra herramienta UUID Generator.
Recuerda: el objetivo de un ID es identificar de forma única. Todo lo demás (rendimiento, legibilidad, orden) son optimizaciones secundarias que dependen de tu caso de uso específico.
Artículos relacionados
Base64: El Puente Entre Binario y Texto que Todo Desarrollador Necesita
Entiende por qué existe Base64, cómo funciona realmente, y cuándo usarlo para data URIs, JWTs, y APIs. Con ejemplos prácticos de navegador y Node.js.
Generación de Contraseñas Seguras: Más Allá de los Símbolos Raros
Descubre por qué la longitud vence a la complejidad, qué es la entropía real, y cómo crear políticas de contraseñas que los usuarios no odien.