UUID v4 y UUID v7 son ambos identificadores de 128 bits, y ahí termina el parecido. v4 dedica 122 bits a la aleatoriedad. v7 coloca delante una marca de tiempo Unix de 48 bits en milisegundos, luego 4 bits de versión, 74 bits aleatorios y 2 bits de variante. El número de versión no es una clasificación. Es una declaración sobre qué puede filtrar el identificador y cómo se ordena.
Ambos están definidos en RFC 9562, que sustituyó a RFC 4122 en 2024. La elección práctica se reduce a tres preguntas: ¿necesita el identificador ordenarse por fecha de creación?, ¿va a acabar en una clave primaria de un B-tree?, y ¿es aceptable que la fecha de creación sea legible por cualquiera que tenga el valor? Esta guía responde a esas preguntas con la estructura de bytes en lugar de con una regla general.
La estructura de 128 bits, lado a lado
Todo UUID ocupa 16 bytes, y los campos se definen por posición, no por los guiones del texto. El byte 6 contiene la versión en su nibble alto. El byte 8 contiene la variante en sus dos bits superiores. Todo lo demás depende de la versión. En la cadena canónica, el decimotercer carácter hexadecimal es la versión y el decimoséptimo es la variante.
Para v4, los 122 bits que no son versión ni variante son aleatorios. Para v7, los primeros 48 bits son la marca de tiempo Unix en milisegundos, big-endian, y por eso los valores v7 se ordenan por orden de creación como bytes en bruto. Los 74 bits restantes son aleatorios por defecto, aunque RFC 9562 permite que algunos de ellos lleven un contador para lograr monotonicidad dentro de un mismo milisegundo.
| Campo | Bits | v4 | v7 |
|---|---|---|---|
| Bytes 0-5 | 48 | Aleatorio | unix_ts_ms, big-endian |
| Nibble alto del byte 6 | 4 | 0100 (versión 4) | 0111 (versión 7) |
| Nibble bajo del byte 6 y byte 7 | 12 | Aleatorio | rand_a |
| Bits superiores del byte 8 | 2 | variante 10x | variante 10x |
| Bytes 8-15 restantes | 62 | Aleatorio | rand_b |
| Total de bits aleatorios | 122 | 122 | 74 |
v4 en detalle
Un UUID v4 es un número aleatorio con seis bits fijos. El nibble alto del byte 6 es 0100, así que el decimotercer carácter de la cadena canónica siempre es 4. Los dos bits superiores del byte 8 son 10, así que el decimoséptimo carácter siempre es 8, 9, a o b. Esos son los únicos caracteres predecibles; los otros 30 dígitos hexadecimales son aleatorios.
La aleatoriedad es la propiedad de seguridad. Un valor v4 no lleva marca de tiempo, ni secuencia, ni identificador de máquina, así que no revela nada sobre cuándo ni dónde se creó. Eso lo convierte en una opción razonable para un identificador público que no debe ser enumerable, siempre que lo genere una fuente aleatoria criptográficamente segura. Un espacio de 122 bits hace que las colisiones sean despreciables: mil millones de valores dan una probabilidad de colisión de aproximadamente 2^-63.
- Nibble de versión: siempre 4 en la posición 13 de la cadena canónica
- Variante: siempre 8, 9, a o b en la posición 17
- Bits aleatorios: 122, de un CSPRNG
- No se ordena por nada significativo; el orden es aleatorio
- No revela fecha de creación ni secuencia
v7 en detalle
Un UUID v7 está ordenado por tiempo. Los bytes 0 a 5 contienen el número de milisegundos desde la época Unix, big-endian. El nibble alto del byte 6 es 0111. El nibble bajo del byte 6 y todo el byte 7 forman rand_a, los dos bits superiores del byte 8 son la variante, y los 62 bits restantes son rand_b. Eso son 74 bits aleatorios más una marca de tiempo legible a simple vista.
La marca de tiempo es de 48 bits, suficiente para milisegundos hasta el año 10889, así que el desbordamiento no es una preocupación práctica. Dos valores v7 creados en el mismo milisegundo difieren solo en sus bits aleatorios o de contador, y un valor creado después siempre compara como mayor, como cadena de bytes, que uno creado antes, siempre que el reloj no retroceda. Ese orden es precisamente el objetivo.
| Byte | Contenido | Ejemplo |
|---|---|---|
| 0-5 | Milisegundos Unix, big-endian | 0192f3c4a1b2 |
| Nibble alto del byte 6 | Versión 7 | 7 |
| Nibble bajo del byte 6 + byte 7 | rand_a, 12 bits | a1b |
| 2 bits superiores del byte 8 | Variante 10 | 8, 9, a o b |
| Byte 8 (6 bits bajos) a 15 | rand_b, 62 bits | Aleatorio o contador |
Ordenación y monotonicidad dentro de un milisegundo
La ordenación lexicográfica del texto canónico coincide con el orden de bytes, porque los guiones están en posiciones fijas y los dígitos hexadecimales se ordenan igual que sus valores cuando están en minúscula. Ordena por la forma de texto o por los 16 bytes y obtendrás el orden de creación. Eso se cumple entre máquinas sin coordinación, y por eso v7 resulta atractivo para registros de eventos y para identificadores que forman parte de una clave de ordenación.
El hueco está dentro de un mismo milisegundo. Por defecto, dos valores creados en el mismo milisegundo se ordenan por sus bits aleatorios, lo que no es el orden de creación. RFC 9562 describe el uso del campo rand_a, o de los bits aleatorios más a la izquierda, como contador sembrado aleatoriamente en cada tic de la marca de tiempo. Con ese método, los valores son monotónicos a efectos prácticos dentro de un milisegundo, a costa de algo menos de aleatoriedad y de un contador compartido que los productores concurrentes deben manejar con cuidado.
- Entre milisegundos: el orden está garantizado mientras el reloj sea monotónico
- Dentro de un milisegundo: orden aleatorio salvo que se use un contador
- Método de contador: sembrar aleatoriamente por tic, incrementar por valor
- Retroceso del reloj: puede romper el orden, así que usa una fuente de reloj monotónica
- Concurrencia: incrementa el contador de forma atómica o pierdes la propiedad
Coste de índice en un B-tree
Una clave primaria v4 aleatoria se inserta en una posición aleatoria del índice. En un B-tree eso significa tocar una página hoja distinta en casi cada inserción, expulsar páginas del buffer pool y dividir páginas por el medio, lo que las deja medio llenas. El índice crece más, la amplificación de escritura aumenta y las tasas de acierto de caché caen a medida que crece la tabla.
Una clave v7 se inserta cerca del borde derecho, así que el conjunto de trabajo se mantiene pequeño y las divisiones de página ocurren al final, donde son baratas. La contrapartida es la contención: todos los escritores quieren la misma página del extremo derecho. En una sola base de datos eso suele estar bien con unos cientos de escrituras por segundo; con una concurrencia muy alta se convierte en un punto caliente, y las mitigaciones habituales son el batching, la intercalación a nivel de conexión o un poco de jitter en los bits altos.
| Propiedad | Clave v4 aleatoria | Clave v7 ordenada por tiempo |
|---|---|---|
| Ubicación de inserción | Página hoja aleatoria | Página hoja del extremo derecho |
| Divisiones de página | Frecuentes, en medio de la página | Raras, al final |
| Localidad del buffer pool | Pobre en índices grandes | Buena |
| Tamaño del índice | Mayor, menor factor de llenado | Compacto |
| Punto caliente de escritura | Repartido por el árbol | Concentrado en el borde derecho |
| Escaneo de rango por tiempo | Requiere una columna aparte | Nativo |
Privacidad y la filtración de la marca de tiempo
Un valor v7 dice a cualquiera que lo tenga, aproximadamente, cuándo se creó, al milisegundo, y por tanto cuántos otros registros se crearon en torno a ese momento. Eso está bien para claves internas e ids de eventos. No está bien para un identificador público que no debería revelar volumen de registros, tasa de pedidos o la antigüedad de una cuenta.
Si necesitas tanto ordenación como opacidad, las opciones son mantener v7 interno y exponer un id público aleatorio aparte, o usar v4 para todo lo que salga de tu sistema. No trates un UUID como token de capacidad en ninguna de las dos versiones; ambos son identificadores, no autorización. Un id v4 es impredecible en la práctica, pero aun así debe comprobarse contra el control de acceso en cada petición.
- v7 revela la fecha de creación al milisegundo
- Enumerar valores v7 cercanos revela el volumen de registros
- Mantén v7 interno, expón v4 o un id público aparte
- Ninguna versión es un token de acceso ni un secreto
- Un v7 aleatorizado no existe; la marca de tiempo es obligatoria
Almacenamiento y tamaño de codificación
Dieciséis bytes es la respuesta de almacenamiento para ambas versiones. La forma de texto canónica tiene 36 caracteres, de los cuales 32 son dígitos hexadecimales y 4 son guiones. Almacenar la forma de texto duplica el tamaño y ralentiza las comparaciones, así que usa un tipo UUID nativo cuando exista, BINARY(16) cuando no, y formatea solo en el límite.
SQL Server tiene una particularidad que conviene probar: su tipo uniqueidentifier se ordena en un orden de bytes que no es el mismo que el de la cadena canónica, y el orden de bytes interno difiere de RFC 4122. El código que dependa del orden de v7 debe verificar el comportamiento de ordenación en esa plataforma en lugar de darlo por supuesto. Cuando necesites una forma de texto compacta, base64url sin relleno da 22 caracteres, pero no es una representación estándar de UUID y confundirá a las herramientas que esperan la forma canónica.
| Representación | Tamaño | Notas |
|---|---|---|
| Binaria | 16 bytes | Almacenamiento nativo, comparación más rápida |
| Texto canónico | 36 caracteres | 8-4-4-4-12 con guiones |
| Hex sin guiones | 32 caracteres | Usado en algunas APIs y logs |
| Base64url, sin relleno | 22 caracteres | No es una forma estándar de UUID |
| Base32 | 26 caracteres | Poco común, usado por algunos sistemas de tickets |
Elegir, migrar y probar
Elige v7 cuando el identificador sea una clave de base de datos, cuando quieras ordenar por fecha de creación sin una segunda columna o cuando estés construyendo una tabla con muchas inserciones al final. Elige v4 cuando el valor se exponga públicamente, cuando la fecha de creación sea sensible o cuando el sistema ya almacene una columna de marca de tiempo y no gane nada con la ordenación. Ambas son opciones correctas para restricciones distintas, y mezclarlas en una misma tabla suele ser un error.
Las pruebas deben verificar la estructura, no los valores. Comprueba el nibble de versión, el nibble de variante y la longitud en bytes tras el parseo. Para v7, verifica que la marca de tiempo incrustada caiga dentro de la ventana de la prueba y que dos valores creados en orden comparen correctamente. Nunca hagas snapshot de un UUID literal en una prueba, y nunca verifiques que un v7 sea estrictamente mayor que el anterior salvo que el generador use un contador; sin uno, el orden dentro del mismo milisegundo es aleatorio.
| Situación | Preferir | Motivo |
|---|---|---|
| Clave primaria en una tabla grande | v7 | Localidad e índice más pequeño |
| Identificador de cara al público | v4 | No revela la marca de tiempo |
| Registro de eventos ordenado por tiempo | v7 | Ordenación nativa sin una columna |
| Token de sesión o de restablecimiento | Ninguno | Usa un token aleatorio específico |
| Tabla existente con columna de tiempo | v4 | La ordenación ya está disponible |
| Intercambio entre sistemas | Cualquiera | Ambos son UUID estándar de 128 bits |
Preguntas
- Sí. Ambos son de 128 bits, 16 bytes y 36 caracteres en texto canónico. Solo difiere la estructura interna.
- Entre milisegundos, sí, tanto como bytes en bruto como en texto canónico. Dentro de un mismo milisegundo el orden por defecto es aleatorio salvo que el generador use un contador.
- v7, porque las inserciones se mantienen cerca del borde derecho del B-tree en lugar de dispersarse por él. La contrapartida es un punto caliente de escritura en la última página con una concurrencia muy alta.
- Revela la fecha de creación al milisegundo, y los valores cercanos revelan el volumen de registros. Mantenlo interno o usa v4 para identificadores que salgan de tu sistema.
- No. Un UUID es un identificador. Incluso un valor v4 debe comprobarse contra el control de acceso en cada petición, y un token debe venir de un generador específico con sus propias reglas de vigencia.
¿UUID v4 y v7 tienen el mismo tamaño?+
¿UUID v7 se ordena correctamente?+
¿Cuál es mejor para una clave primaria de base de datos?+
¿UUID v7 filtra información?+
¿Puedo usar un UUID como token de acceso?+
Los ejemplos son sintéticos. No utilices una dirección generada para verificaciones de identidad, registro de cuentas ni entregas.