Generador UUID v7
Genera UUID v7 ordenados por tiempo para claves primarias de bases de datos
…
Formatear
¿Qué es UUID v7?
UUID v7 es un formato de UUID de nueva generación estandarizado en RFC 9562 (mayo de 2024). Codifica una marca de tiempo Unix de 48 bits en milisegundos en los bits más significativos, seguido de marcadores de versión y variante, y llena los bits restantes con datos aleatorios criptográficamente seguros.
Debido a que la marca de tiempo ocupa los bits más significativos, los valores UUID v7 se ordenan cronológicamente — tanto lexicográficamente como numéricamente. Esto los hace excelentes para claves primarias de bases de datos, donde los UUIDs aleatorios (v4) causan fragmentación en el índice B-tree.
Por Qué los UUIDs Aleatorios Fragmentan los Índices de Base de Datos
Los índices B-tree — usados por PostgreSQL, MySQL, SQLite y la mayoría de las otras bases de datos — mantienen las filas ordenadas por valor de clave. Cuando inserta una nueva fila, la base de datos debe colocarla en la posición ordenada correcta dentro del índice.
Con UUID v4 (completamente aleatorio), cada nueva inserción cae en una posición esencialmente aleatoria en el árbol de índice. Esto obliga a la base de datos a leer y reescribir páginas de índice internas constantemente, dividiendo páginas llenas y dejando otras a la mitad. El resultado es un índice fragmentado e inflado que ralentiza tanto las escrituras como las lecturas a medida que crece la tabla.
Distribución de Bits de UUID v7
UUID v7 tiene 128 bits de ancho, divididos en seis campos:
| Bits | Campo | Propósito |
|---|---|---|
| 48 | unix_ts_ms | Marca de tiempo Unix de 48 bits en milisegundos — ocupa toda la mitad superior |
| 4 | ver | Número de versión — siempre 0111 (decimal 7) |
| 12 | rand_a | 12 bits de datos aleatorios criptográficamente seguros |
| 2 | var | Marcador de variante — siempre 10 (variante RFC 4122) |
| 62 | rand_b | 62 bits de datos aleatorios criptográficamente seguros |
La precisión de la marca de tiempo es de 1 milisegundo. Dentro del mismo milisegundo, los valores UUID v7 se ordenan por su sufijo aleatorio — no se garantiza que sean monotónicamente crecientes por debajo del milisegundo, pero son k-ordenables: los IDs generados cercanos en el tiempo se ordenarán cercanos en el índice.
UUID v7 vs UUID v1
Tanto UUID v1 como UUID v7 incorporan una marca de tiempo, pero difieren significativamente en diseño:
| Característica | UUID v7 | UUID v1 |
|---|---|---|
| Época / Base de Tiempo | Época Unix (1 ene. 1970) | Época gregoriana (15 oct. 1582) |
| Precisión de Tiempo | 1 milisegundo | 100 nanosegundos |
| Ordenable | Sí — k-ordenable por diseño | No — los campos de tiempo están mezclados en la distribución UUID |
| Privacidad | No se filtra información del host | Incorpora la dirección MAC del host generador |
| Rendimiento de Índice BD | Excelente — inserciones secuenciales, fragmentación mínima | Deficiente — no secuencial a pesar de la marca de tiempo |
| Estándar | RFC 9562 (2024) | RFC 4122 (2005) |
| Soporte Nativo en Navegador | Aún no (sin crypto.randomUUID v7) | No disponible de forma nativa |
Para cualquier proyecto nuevo que necesite UUIDs ordenados por tiempo, prefiera UUID v7 sobre UUID v1. UUID v1 es legado y filtra información del host.
UUID v7 vs ULID
ULID (Identificador Universalmente Único Lexicográficamente Ordenable) resuelve un problema similar a UUID v7. Aquí se comparan:
- Sigue el estándar UUID RFC 9562 — compatible con todas las herramientas UUID
- Formato hexadecimal con guiones — universalmente reconocido
- Soporte nativo de columna UUID en bases de datos
- 128 bits en total
- Codificación Crockford Base32 — 26 caracteres, ligeramente más compacto
- Insensible a mayúsculas y evita caracteres ambiguos (I, L, O, U)
- Más legible para humanos a simple vista
- Requiere una biblioteca — sin soporte nativo de plataforma
Si ya está en un ecosistema UUID (columna uuid de PostgreSQL, APIs REST que retornan UUIDs), use UUID v7. Si está comenzando de cero y prefiere una codificación más amigable para humanos, ULID es una alternativa razonable.
Usando UUID v7 en Bases de Datos
UUID v7 aún no es generado nativamente por la mayoría de las bases de datos, pero puede almacenarse en columnas UUID estándar y generarse en el código de la aplicación o mediante extensiones:
uuid. La extensión pg-uuidv7 agrega una función uuid_generate_v7() del lado del servidor si necesita IDs generados por la BD.BINARY(16) o CHAR(36). Genere en el código de la aplicación. MySQL 8.0+ tiene soporte de UUID ordenado mediante UUID_TO_BIN(UUID(), 1) para v1, pero v7 requiere generación a nivel de aplicación.TEXT (36 caracteres) o BLOB (16 bytes). Genere en el código de la aplicación. La ordenación lexicográfica en TEXT funciona correctamente porque UUID v7 usa un prefijo de marca de tiempo de ancho fijo.Ejemplos de Código
UUID v7 aún no está disponible mediante crypto.randomUUID(). Use una biblioteca como uuidv7 (npm) hasta que llegue el soporte nativo:
function generateUuidV7() {
const buf = new Uint8Array(16)
crypto.getRandomValues(buf)
const ms = BigInt(Date.now())
// Embed 48-bit Unix ms timestamp
buf[0] = Number((ms >> 40n) & 0xFFn)
buf[1] = Number((ms >> 32n) & 0xFFn)
buf[2] = Number((ms >> 24n) & 0xFFn)
buf[3] = Number((ms >> 16n) & 0xFFn)
buf[4] = Number((ms >> 8n) & 0xFFn)
buf[5] = Number(ms & 0xFFn)
// Set version 7 (0111xxxx)
buf[6] = (buf[6] & 0x0F) | 0x70
// Set variant (10xxxxxx)
buf[8] = (buf[8] & 0x3F) | 0x80
const hex = [...buf].map(b => b.toString(16).padStart(2, '0')).join('')
return `${hex.slice(0,8)}-${hex.slice(8,12)}-${hex.slice(12,16)}-${hex.slice(16,20)}-${hex.slice(20)}`
}
// Node.js 20+ built-in
// import { randomUUID } from 'node:crypto' // v4 only — no v7 yet in stdlib# pip install uuid7 import uuid_extensions uid = uuid_extensions.uuid7() print(uid) # e.g. 018e2b3d-1a2b-7000-8000-abc123456789 print(uid.time) # Unix ms timestamp embedded in the UUID # Or as a plain string from uuid_extensions import uuid7str print(uuid7str())
-- PostgreSQL 13+ extension-free implementation
CREATE OR REPLACE FUNCTION uuid_generate_v7()
RETURNS uuid
LANGUAGE sql
AS $$
SELECT encode(
set_bit(
set_bit(
overlay(
uuid_send(gen_random_uuid())
PLACING substring(int8send(floor(extract(epoch FROM clock_timestamp()) * 1000)::bigint) FROM 3)
FROM 1 FOR 6
),
52, 1
),
53, 1
),
'hex'
)::uuid;
$$;
-- Usage as a default primary key
CREATE TABLE events (
id uuid PRIMARY KEY DEFAULT uuid_generate_v7(),
payload jsonb,
created_at timestamptz DEFAULT now()
);function extractTimestamp(uuid: string): Date {
const hex = uuid.replace(/-/g, '')
const ms = parseInt(hex.slice(0, 12), 16) // first 48 bits = ms timestamp
return new Date(ms)
}
const uid = '018e2b3d-1a2b-7000-8000-abc123456789'
console.log(extractTimestamp(uid).toISOString())
// → "2024-03-15T10:22:05.259Z"