UUID y ULID son mecanismos para emitir identificadores únicos en sistemas distribuidos o bases de datos. Parecen similares, pero difieren en estandarización, eficiencia de almacenamiento, legibilidad y exposición de información. En este artículo comparo tres enfoques —UUID v4, UUID v7 y ULID—, detallo sus características y criterios de selección, y cierro con apuntes sobre UUID v1/v6.

Estas herramientas del sitio funcionan íntegramente en el navegador y permiten generar o analizar cada formato:

  • Generador de UUID v4 (/tools/uuid/, interfaz actualmente en japonés e inglés).
  • Generador de UUID v7 (/tools/uuidv7/).
  • Lector de marcas temporales de UUID v7 (/tools/ulidtsextractor).
  • Generador de ULID (/tools/ulid/).
  • Lector de marcas temporales de ULID (/tools/ulidtsextractor).

UUID v4: aleatorio puro

UUID v4 dedica 122 de los 128 bits a aleatoriedad y se caracteriza por una probabilidad de colisión extremadamente baja. La contracara es que no sigue el orden temporal, por lo que en índices B-Tree provoca divisiones frecuentes y mala localidad de inserción. Es ideal para ID de sesión, claves de un solo uso y otros casos donde basta con un valor completamente aleatorio.


UUID v7: evolución ordenable temporalmente

Estandarizado en 2024 por la RFC 9562, UUID v7 reserva los primeros 48 bits para el tiempo UNIX (milisegundos) y rellena el resto con aleatoriedad. La comparación lexicográfica coincide con el orden de generación, de modo que funciona bien como clave primaria con buena localidad. Otra ventaja es que se almacena sin cambios en tipos uuid o BINARY(16) existentes.

  • Longitud efectiva de aleatoriedad: descontando los bits de versión y variante, quedan alrededor de 74 bits aleatorios. Algunas implementaciones emplean generación monótona dentro del mismo milisegundo para evitar colisiones, sin comprometer la distribución estadística.
  • Riesgos de exposición: a partir del ID se puede estimar el momento de generación (precisión de milisegundos). Si la aleatoriedad es débil y se emiten muchos IDs cercanos, podría ser más fácil deducir vecinos. En sistemas internos suele ser aceptable, pero en endpoints públicos conviene precaución cuando no se desea revelar un patrón casi secuencial.

ULID: identificadores amigables para personas

Propuesto en 2016 como estándar de facto, ULID utiliza Base32 de Crockford en 26 caracteres, excluyendo letras ambiguas (O/I/L/1) y facilitando copiar, pegar o embedir en URLs. Igual que v7, los 48 bits iniciales codifican tiempo en milisegundos, por lo que el orden lexicográfico coincide con el temporal.

  • Garantía de orden: comparar cadenas ULID respeta siempre el orden cronológico (la generación monótona está ampliamente soportada).
  • Formato de almacenamiento: TEXT(26) favorece la lectura, pero guardar como binario exige convertir entre Base32 y 16 bytes. En muchas bases de datos no es compatible con el tipo uuid nativo.
  • Riesgo de exposición: al igual que v7, revela el instante de emisión. Para APIs públicas donde no conviene mostrar el ritmo de emisión, analízalo con cautela.

Tabla comparativa (visión práctica)

Elemento UUID v4 UUID v7 ULID
Estandarización RFC 4122 RFC 9562 (2024) No estándar (de facto)
Distribución de bits 128 bits con ≈122 bits de aleatoriedad 48 bits = UNIX ms + resto aleatorio (posible modo monótono) 48 bits = UNIX ms + 80 bits aleatorios
Entropía aleatoria ≈122 bits ≈74 bits (tras versión/variante) 80 bits
Formato de cadena 36 caracteres (hex con guiones) 36 caracteres (hex con guiones) 26 caracteres (Base32 de Crockford)
Orden natural (texto) × (aleatorio) ○ (lexicográfico = cronológico) ○ (lexicográfico = cronológico)
Orden natural (binario) × ○ (comparar BINARY(16) mantiene el orden temporal) ○ (si los 6 bytes de tiempo van al inicio en big-endian, memcmp respeta el orden)
Almacenamiento en BD uuid / BINARY(16) ideales uuid / BINARY(16) ideales TEXT(26) legible; BINARY(16) requiere conversión / no compatible con tipos UUID nativos
Localidad de índice Mala (inserciones aleatorias) Buena (apéndices sucesivos; vigilar hotspots) Buena (apéndices sucesivos; vigilar hotspots)
Coste de consultas por igualdad Bajo (comparar 16 B) Bajo (comparar 16 B) En TEXT algo mayor (collation); en BINARY bajo
Costo de codificación Hex ↔ 16 B (ligero) Hex ↔ 16 B (ligero) Base32 ↔ 16 B (más pesado)
Legibilidad / apto para URL Baja Baja Alta
Exposición de información (timestamp) Ninguna Sí (milisegundos) Sí (milisegundos)
Usos principales ID de sesión, claves de un solo uso Claves primarias en BD, IDs de eventos, registros temporales IDs públicos y legibles, logging con visibilidad

Nota adicional (hotspots en v7/ULID): si todos los apéndices caen en un único shard o líder, el extremo del B-Tree puede calentarse. Considera barajar ligeramente los bits altos (prefix shuffle) o combinar con una clave de partición.


Notas de implementación en bases de datos (PostgreSQL / MySQL / SQLite)

  • PostgreSQL
    • v4/v7: el tipo uuid es ideal. Inserta v7 como cadena y conviértelo a uuid para aprovechar el orden.
    • ULID: usa char(26)/text o bytea(16) tras convertir desde Base32.
  • MySQL/InnoDB
    • BINARY(16) es rápido y compacto (v4/v7). Para v7, mantener el orden big-endian preserva la secuencia temporal.
    • ULID: CHAR(26) legible o BINARY(16) con conversión.
  • SQLite
    • No tiene tipo UUID específico. Usa BLOB(16) o TEXT; los BLOB indexados son relativamente eficientes.

Consideraciones adicionales de seguridad

  • Resistencia a la predicción: v7/ULID comparten bits de tiempo; si emites muchos IDs en el mismo milisegundo con aleatoriedad débil, aumenta el riesgo relativo de deducir vecinos. Usa PRNG criptográficamente seguros y minimiza sesgos en la generación monótona.
  • Filtración de metadatos: el ID revela el instante de generación e incluso un indicio del volumen emitido. Para APIs públicas, separa los IDs internos de los que expones externamente.

Resumen (guía de selección)

  • UUID v4: completamente aleatorio. Sin orden. El riesgo de colisión es teórico y muy bajo. Útil para sesiones, tokens CSRF, etc.
  • UUID v7: UUID ordenable y estandarizado. Excelente para claves internas y bases de datos. Con generación monótona, las colisiones son prácticamente evitables.
  • ULID: más amigable para humanos. Buen candidato cuando la visibilidad en URL o logs es relevante; para claves internas suele ganar v7. La generación monótona cubre las colisiones prácticas.

Apéndice: UUID v1 / v6 (resumen)

  • UUID v1 (tiempo + MAC) 60 bits de timestamp (100 ns) más un identificador de nodo (a menudo la MAC). Ofrece fuerte orden temporal, pero puede exponer información del host por la MAC y requiere cuidados con la inversión del reloj. Por motivos de privacidad hoy suele desaconsejarse.

  • UUID v6 (reordenación de v1) Reordena el timestamp de v1 en big-endian para facilitar el orden lexicográfico. Históricamente se planteó como un «v1 fácil de ordenar», pero la estandarización convergió en v7. En diseños nuevos es habitual preferir v7 sobre v6.


Anexo: lista de verificación para la implementación

  • Usa fuentes de aleatoriedad criptográficamente seguras provistas por el sistema operativo.
  • Para v7/ULID, la generación monótona debe evitar colisiones en el mismo milisegundo sin introducir sesgos significativos.
  • Prioriza uuid/BINARY(16) en bases de datos y evita degradaciones por collation en columnas de texto.
  • Define una política para IDs externos (si ocultas la secuencia, separa IDs internos y públicos).

En la práctica actual, v7 es el primer candidato para claves primarias internas, mientras que ULID se considera cuando la legibilidad externa o restricciones técnicas lo exigen. Si un requisito especial impide exponer la hora, entonces opta por v4 u otra alternativa.