UUID vs ULID vs Nano ID vs IDs autoincrementales

Cinco esquemas de ID habituales comparados en lo que importa en la práctica: tamaño, orden, probabilidad de colisión, comportamiento en la base de datos y lo que delata cada ID.

Elegir un formato de ID se reduce a tres preguntas. ¿Los IDs deben ordenarse por fecha de creación? ¿Aparecerán en URL o en API públicas? ¿Y la base de datos los guardará en una columna nativa de 128 bits o como texto? UUID v7, estandarizado en el RFC 9562 en mayo de 2024, cambió la respuesta para muchos proyectos: ofrece el orden temporal que hizo popular a ULID, en un formato que ya aceptan todas las columnas y bibliotecas de UUID.

De un vistazo

AutoincrementalUUID v4UUID v7ULIDNano ID
Ejemplo1042f47ac10b-58cc-4372-a567-0e02b2c3d47901926f3a-7c2b-7d4e-9a1f-3b5c8d2e6f1001J9QKMZ1BFN79M7SVBJ6JWVRGV1StGXR8_Z5jdHi6B-myT
Tamaño binario4 u 8 bytes16 bytes16 bytes16 bytesSe guarda como texto
Longitud en textoHasta 10 o 19 dígitos36362621 por defecto
Bits aleatoriosNinguno122Hasta 7480126 por defecto
Se ordena por fecha de creaciónSíNoSí, al milisegundoSí, al milisegundoNo
Se genera sin la base de datosNoSíSíSíSí
EspecificaciónFunción de la base de datosRFC 9562RFC 9562Especificación comunitariaBiblioteca
RevelaCantidad y ordenNadaFecha de creaciónFecha de creaciónNada

Cómo se construye cada uno

  • Autoincremental es un contador que mantiene la base de datos. Un INT de 32 bits con signo termina en 2 147 483 647, y un BIGINT con signo, en 9 223 372 036 854 775 807. La aplicación solo conoce el ID después de la inserción, a menos que reserve antes valores de una secuencia.
  • UUID v4 son 122 bits aleatorios más 6 bits fijos que marcan la versión y la variante.
  • UUID v7 empieza con una marca de tiempo Unix de 48 bits en milisegundos, seguida de la versión, 12 bits llamados rand_a, la variante y 62 bits llamados rand_b. El RFC 9562 permite que un generador use parte de esos bits como contador, para que los IDs creados en el mismo milisegundo sigan saliendo en orden. El generador de este sitio lo hace con los 12 bits de rand_a.
  • ULID es una marca de tiempo de 48 bits en milisegundos más 80 bits aleatorios, escrita como 26 caracteres en el Base32 de Crockford: 10 para el tiempo y 16 para la parte aleatoria. El alfabeto excluye I, L, O y U, y no distingue mayúsculas de minúsculas. En modo monótono, un ULID creado en el mismo milisegundo que el anterior toma la parte aleatoria de ese ULID y le suma uno.
  • Nano ID es una cadena de caracteres aleatorios, por defecto 21 tomados del alfabeto de 64 caracteres A-Z a-z 0-9 _ -. Cada carácter aporta 6 bits, lo que da 126 bits. Tanto la longitud como el alfabeto se pueden cambiar, y el número de bits aleatorios cambia con ellos.

Un ULID y un UUID son números de 128 bits escritos de forma distinta. 01J9QKMZ1BFN79M7SVBJ6JWVRG y 01926f3a-7c2b-7d4e-9a1f-3b5c8d2e6f10 son el mismo valor, en Base32 y en hexadecimal. Por eso un ULID puede guardarse en una columna UUID nativa, como el tipo uuid de PostgreSQL, y conserva allí su orden. Eso sí, un ULID que no nació como UUID v7 tiene bits arbitrarios donde deberían ir la versión y la variante, así que un validador de UUID estricto puede rechazarlo.

Probabilidad de colisión, calculada

Para los IDs puramente aleatorios se aplica la aproximación del cumpleaños: con n IDs tomados de N = 2 elevado al número de bits aleatorios, la probabilidad de que haya al menos un duplicado es de aproximadamente 1 − e^(−n²/2N), y llega al 50% cerca de n = 1,18 × √N.

EsquemaBits aleatoriosIDs para un 50% de probabilidad de duplicadoProbabilidad de duplicado entre 1000 millones de IDs
UUID v41222,7 × 10^189,4 × 10^-20
Nano ID, 21 caracteres1261,1 × 10^195,9 × 10^-21
Nano ID, 10 caracteres601,3 × 10^9Alrededor del 35%

La última fila es la que conviene recordar: acortar un Nano ID para tener URL más bonitas reduce muy deprisa la resistencia a las colisiones, así que haga estas cuentas antes de elegir una longitud.

En UUID v7 y ULID, dos IDs solo pueden colisionar si llevan el mismo milisegundo, así que n es el número de IDs creados en un milisegundo por generadores que no comparten contador. Con 1000 de esos IDs en el mismo milisegundo, la probabilidad de duplicado es de unos 2,6 × 10^-17 con 74 bits aleatorios y de 4,1 × 10^-19 con los 80 de ULID. En la práctica, el riesgo de todos estos esquemas es una fuente aleatoria débil, como Math.random, y no la aritmética.

Comportamiento en los índices de la base de datos

Las claves primarias suelen vivir en un índice B-tree. Cuando cada clave nueva es mayor que la anterior, como ocurre con los autoincrementales, UUID v7 y ULID, las inserciones caen en el borde derecho del índice, las páginas se llenan por completo y las filas escritas recientemente quedan juntas. Las claves aleatorias, UUID v4 y Nano ID, caen en cualquier parte: las páginas se dividen, quedan medio vacías y, cuando el índice ya no cabe en memoria, cada inserción puede suponer una lectura aleatoria.

El efecto es mayor en MySQL y MariaDB con InnoDB, que guardan las propias filas en el orden de la clave primaria, de modo que las claves aleatorias dispersan la tabla y no solo el índice. PostgreSQL guarda las filas en un heap aparte, así que allí la diferencia se limita al índice.

El formato de almacenamiento importa tanto como el esquema:

  • Guarde los UUID y los ULID en un tipo nativo de 16 bytes cuando exista: uuid en PostgreSQL, UUID en MariaDB 10.7 y posteriores, BINARY(16) en MySQL. CHAR(36) ocupa más del doble y agranda todos los índices.
  • En MySQL, no pase el indicador de intercambio a UUID_TO_BIN(uuid, 1) con valores v7. Reordena los campos de tiempo pensando en los UUID v1 y rompería el orden de los v7.
  • El tipo uniqueidentifier de SQL Server compara primero los seis últimos bytes, así que los valores v7 guardados en él no se ordenan por tiempo. Encontrará los detalles en UUID vs GUID.
  • Los Nano ID son cadenas y se guardan como CHAR o VARCHAR, con una intercalación (collation) binaria si hay que distinguir mayúsculas de minúsculas.

URL y lo que revela un ID

Los cinco formatos son seguros en una URL sin codificación porcentual: los dígitos hexadecimales, los guiones, el Base32 de Crockford y el alfabeto de Nano ID son caracteres no reservados según el RFC 3986. La diferencia está en la longitud (36, 26 o 21 caracteres) y en las mayúsculas. Los UUID y los ULID no distinguen mayúsculas de minúsculas, así que sobreviven a los sistemas que las cambian; los Nano ID, no.

Lo que revela un ID suele ser el factor decisivo:

  • Autoincremental revela volumen. Si su factura es la 1042 hoy y la 1342 una semana después, la empresa emitió unas 300 facturas esa semana. Los IDs secuenciales también hacen trivial probar registros vecinos, lo que convierte cualquier comprobación de acceso olvidada en una fuga de datos.
  • UUID v7 y ULID revelan cuándo se creó el registro, al milisegundo. En pedidos rara vez es un problema; en cuentas de usuario o historiales médicos puede serlo. Pegue un UUID v7 en el inspector del generador de UUID v7 y verá la marca de tiempo.
  • UUID v4 y Nano ID no revelan nada sobre cuándo ni dónde se crearon.

Ninguno de estos IDs es un secreto. Un ID imposible de adivinar dificulta el sondeo, pero no sustituye a la comprobación de si el usuario tiene permiso para ver el registro.

Cuál elegir

  • Tablas internas en una sola base de datos que los usuarios nunca ven: BIGINT autoincremental. Es la clave más pequeña y sencilla.
  • Claves primarias creadas por la aplicación o entre servicios, cuando hay una columna UUID nativa disponible: UUID v7.
  • Identificadores públicos cuya fecha de creación debe permanecer privada, IDs de correlación y nombres de archivo: UUID v4, del generador de UUID v4 o de crypto.randomUUID().
  • IDs cortos en URL donde el orden no importa: Nano ID con su longitud por defecto o una cercana.
  • ULID cuando su stack ya lo usa o prefiere la forma de texto más corta e insensible a mayúsculas. En proyectos nuevos, UUID v7 cubre lo mismo con un formato estándar.

Una combinación habitual reúne lo mejor de ambos mundos: una clave primaria BIGINT o UUID v7 para joins e índices, más un ID público aleatorio aparte, con índice único, para las URL y las API.

Preguntas Frecuentes

¿Es ULID mejor que UUID v7?

Se construyen igual: una marca de tiempo de 48 bits en milisegundos seguida de bits aleatorios, 128 bits en total. UUID v7 es un estándar del IETF que encaja en las columnas y los validadores de UUID. ULID tiene una forma de texto más corta e insensible a mayúsculas, y 80 bits aleatorios en lugar de hasta 74.

¿Es Nano ID tan único como un UUID?

Con su longitud por defecto de 21 caracteres, sí: tiene 126 bits aleatorios frente a los 122 de UUID v4. Los Nano ID más cortos pierden esa ventaja rápidamente; con 10 caracteres hay un 50% de probabilidad de duplicado tras unos 1300 millones de IDs.

¿Pueden colisionar los UUID v7?

Solo pueden colisionar IDs creados en el mismo milisegundo por generadores independientes. Con 1000 de ellos en un milisegundo, la probabilidad es de unos 2,6 × 10^-17, así que una buena fuente aleatoria importa mucho más que el formato.

¿Por qué los UUID aleatorios son lentos como claves primarias?

Cada clave nueva cae en un punto aleatorio del índice B-tree, lo que provoca divisiones de página, páginas medio vacías y más lecturas de disco en tablas grandes. Los IDs ordenados por tiempo, como UUID v7 o ULID, se añaden al final.