El UUID v4 aleatorio ha sido la elección por defecto durante quince años, y en bases de datos modernas resulta ser una decisión costosa: sus identificadores llegan en orden completamente aleatorio, lo que fragmenta índices B-tree y destroza el caché de páginas. UUIDv7 —estandarizado oficialmente en RFC 9562 (2024)— y ULID atacan ese problema desde ángulos ligeramente distintos. Esta es la comparación que te falta antes de elegir clave primaria.
El problema del v4: inserciones al azar
Un B-tree mantiene los datos ordenados por clave. Cuando cada nueva fila trae un ID impredecible, su posición de inserción es aleatoria:
- La página destino puede no estar en memoria → lectura de disco.
- Puede estar llena → split de página.
- El buffer pool se llena de páginas "calientes" dispersas que solo se usan una vez.
En PostgreSQL esto tiene nombre conocido: index bloat y write amplification. Con millones de filas diarias, la diferencia frente a IDs secuenciales es medible en órdenes de magnitud de escritura. Los autoincrementales evitaban el problema... a cambio de revelar volumen de negocio en URLs (/pedidos/48392 dice cuántos pedidos llevas) y de requerir coordinación central para asignar IDs.
UUIDv7: timestamp + aleatoriedad
La versión 7 empaqueta un timestamp Unix en milisegundos de 48 bits seguido de 74 bits aleatorios:
018f 6a1c b2d4 7xxx yxxx xxxxxxxxxxxx
└── timestamp ms ──┘└─ rand_a + rand_b ─┘
Las propiedades resultantes:
- Ordenables cronológicamente: dos v7 generados en momentos distintos ordenan igual como strings y como bytes.
ORDER BY id≈ORDER BY created_atsin columna extra. - Inserción casi secuencial: los nuevos registros caen siempre "al final" del índice. Adiós splits aleatorios.
- Únicos sin coordinación: los bits aleatorios garantizan unicidad incluso entre máquinas generando miles por milisegundo.
- Fecha gratis: decodificar los primeros bytes revela cuándo nació el registro.
Generación nativa ya disponible:
crypto.randomUUID(); // siempre v4 (por ahora)
// v7 con librería o implementación manual:
function uuidv7() {
const ts = Date.now();
const bytes = crypto.getRandomValues(new Uint8Array(10));
const hex = [
ts.toString(16).padStart(12, "0").slice(0, 12),
[...bytes].map(b => b.toString(16).padStart(2, "0")).join("")
].join("");
// fija versión 7 y variante según RFC 9562
return `${hex.slice(0,8)}-${hex.slice(8,12)}-7${hex.slice(13,16)}-a${hex.slice(17,20)}-${hex.slice(20,32)}`;
}
PostgreSQL 18 incluye uuidv7() nativo; las extensiones (pg_uuidv7) y librerías cliente cubren versiones anteriores.
ULID: el primo con Crockford Base32
ULID (Universally Unique Lexicographically Sortable Identifier) resolvió el mismo problema en 2016, con dos diferencias de diseño:
- 128 bits igual, pero codifica todo en Crockford Base32 de 26 caracteres:
0123456789ABCDEFGHJKMNPQRSTVWXYZ(sin I, L, O, U para evitar confusiones visuales). - 48 bits de timestamp + 80 de aleatoriedad, sin guiones ni estructura interna visible:
01ARZ3NDEKTSV4RRFFQ69G5FAV
Ventajas prácticas: 26 caracteres frente a 36, ordenable como string en cualquier sistema sin parsear, y más corto en URLs. Desventaja: no es estándar ISO/RFC, así que ecosistemas fuera de JS/Go/Java requieren librería, mientras UUIDv7 es ahora norma internacional con soporte creciente en runtimes y bases de datos.
Comparativa directa
| Propiedad | UUID v4 | UUID v7 | ULID |
|---|---|---|---|
| Aleatoriedad | 122 bits | 74 bits | 80 bits |
| Orden temporal | No | Sí (ms) | Sí (ms) |
| Tamaño textual | 36 chars | 36 chars | 26 chars |
| Estándar | RFC 9562 | RFC 9562 | Especificación propia |
| Fecha embebida | No | Sí | Sí |
| Soporte nativo DB | Universal | Creciente | Por extensión |
Nota sobre los bits perdidos: v7 conserva unicidad estadística sobrada para cualquier carga real; la colisión requiere coincidencia de milisegundo Y de 74 bits aleatorios entre generadores concurrentes. No es un escenario de preocupación práctica.
Cuándo seguir con v4
No todo es migración: tokens opacos de sesión, claves API expuestas, identificadores en contextos donde filtrar por fecha sería información sensible — ahí la ausencia total de estructura del v4 es precisamente la virtud. Un v7 en URLs públicas regala el timestamp de creación.
Y una advertencia de seguridad aplicada: si usas IDs ordenables en endpoints públicos, combina con autorización correcta — el orden no sustituye permisos, solo hace predecibles los vecinos (/facturas/018f6a1c... invita a probar el siguiente).
Genera y verifica identificadores
Nuestro generador de UUID produce v4 y variantes al instante para pruebas, y si necesitas repasar cómo funcionan internamente los v4 con crypto.randomUUID(), tienes la guía completa de UUID en JavaScript.
Preguntas frecuentes
¿Puedo migrar una tabla existente de v4 a v7? Puedes usar v7 para registros NUEVOS sin tocar los viejos: el índice acepta ambos. Reordenar históricos rara vez compensa.
¿UUIDv7 filtra información? Sí, deliberadamente: expone la fecha de creación. Si eso es un problema en tu dominio, quédate en v4.
¿Snowflake IDs (Twitter)? Misma idea (timestamp + worker + secuencia) pero requieren coordinación de worker IDs entre máquinas. v7/ULID logran lo esencial sin esa infraestructura.
Genera identificadores únicos al instante con nuestro generador de UUID online, gratis y directamente en tu navegador.