Ir al contenido
Toova
Todas las herramientas

UUID v4 vs v7 — Diferencias y Cuándo Usar Cada Uno

Toova

UUID v4 ha sido la opción predeterminada para claves primarias distribuidas durante más de una década. Genera un valor aleatorio de 128 bits, formateado en ocho grupos hexadecimales, listo. Sin coordinación necesaria, probabilidad de colisión prácticamente nula, funciona en todas partes. Entonces, ¿por qué UUID v7 — estandarizado en RFC 9562 en 2024 — se está extendiendo rápidamente por los sistemas de producción en 2026?

La respuesta corta: rendimiento de base de datos. UUID v4 destruye la localidad del índice B-tree. UUID v7 soluciona eso manteniendo todo lo demás que les gusta a los desarrolladores sobre los UUIDs. Este artículo explica qué es cada versión en realidad, por qué la diferencia importa a escala, cuándo elegir cada una y cómo migrar sin tiempo de inactividad.

¿Necesitas generar UUIDs ahora mismo? Prueba el generador de UUID de Toova, que soporta tanto v4 como v7 en bulk. Para otros identificadores aleatorios, el generador de cadenas aleatorias y el generador de contraseñas cubren tokens más cortos.

UUID v4 — Aleatoriedad Pura

La versión 4 de UUID usa un generador de números pseudoaleatorios criptográficamente seguro (CSPRNG) para rellenar 122 de los 128 bits. Los seis bits restantes son fijos: cuatro bits codifican la versión (0100) y dos bits codifican la variante (10). El resultado tiene este aspecto:

f47ac10b-58cc-4372-a567-0e02b2c3d479
              ^^^^
              versión = 4 (aleatorio)

La aleatoriedad es la funcionalidad. Dos sistemas independientes pueden generar UUIDs sin coordinación y nunca colisionan — la probabilidad de una colisión en un conjunto de 2,71 cuatrillones de UUIDs v4 es aproximadamente del 50%, lo que significa que para cualquier aplicación práctica el riesgo es insignificante. No necesitas un servidor de ID centralizado, una secuencia de base de datos ni un bloqueo distribuido.

Cómo se genera v4

import { v4 as uuidv4 } from 'uuid';
const id = uuidv4();
// => 'f47ac10b-58cc-4372-a567-0e02b2c3d479'

La biblioteca uuid (JavaScript), uuid.uuid4() de Python, google/uuid de Go y todo runtime de lenguaje importante delegan en el CSPRNG del SO — /dev/urandom en Linux, CryptGenRandom en Windows. El coste de generación es efectivamente cero.

El problema: la inserción aleatoria destruye los índices B-tree

Un índice B-tree mantiene los datos ordenados. Cuando insertas una nueva fila, la base de datos encuentra dónde encaja la nueva clave en el orden de clasificación y la coloca ahí. Si cada clave nueva es aleatoria, cae en una posición aleatoria en el índice — lo que significa que cada inserción necesita cargar una página diferente del disco en el pool de búferes. Con bajo volumen esto es invisible. Con alto volumen (millones de filas, alta tasa de INSERT), crea un patrón llamado fragmentación de índice: el índice se llena de páginas medio vacías porque cada inserción golpea una ubicación diferente, y el pool de búferes churna constantemente porque el conjunto de trabajo activo abarca todo el índice en lugar de un fragmento reciente predecible.

Los síntomas en producción: la latencia de INSERT aumenta, la sobrecarga de autovacuum crece (PostgreSQL), la presión del checkpoint aumenta y el rendimiento de lectura para registros recientes se degrada porque "reciente" ya no se mapea a ninguna localidad en el índice. Esto no es hipotético — es un punto de dolor bien documentado en tablas grandes de PostgreSQL y MySQL con claves primarias UUID v4.

UUID v7 — Aleatoriedad Ordenada por Tiempo

UUID v7 fue diseñado explícitamente para resolver el problema de fragmentación B-tree. Codifica un timestamp Unix de 48 bits en milisegundos en los bits más significativos, seguidos de bits de versión, 12 bits aleatorios, bits de variante y 62 bits más aleatorios. Aleatoriedad total: 74 bits — todavía muy por encima de lo necesario para prevenir colisiones.

018f4e6b-a23c-7d45-9abc-0e02b2c3d479
 ^^^^^^^^^^^^^^
 timestamp Unix de 48 bits (precisión en ms)
                   ^
                   versión = 7

Como el timestamp ocupa los bits altos, los UUIDs generados más tarde se ordenan después de los UUIDs generados anteriormente. Las inserciones siempre se añaden al borde derecho del índice. La base de datos solo necesita mantener la página de índice más reciente activa en el pool de búferes, no todo el índice. Con tasas de INSERT altas, esto solo puede reducir la latencia de escritura en un 30–60% y reducir las E/S en un orden de magnitud en tablas con decenas de millones de filas.

Cómo se genera v7

import { v7 as uuidv7 } from 'uuid';
const id = uuidv7();
// => '018f4e6b-a23c-7d45-9abc-0e02b2c3d479'

La misma biblioteca uuid añadió soporte para v7 en la versión 10. La biblioteca estándar de Python lo añadió en la 3.14. PostgreSQL 17 incluye uuidv7() como función incorporada. Si estás en un stack más antiguo, varias bibliotecas pequeñas proporcionan generación v7 sin dependencias.

Monotonicidad sub-milisegundo

¿Qué ocurre cuando se generan dos UUIDs v7 dentro del mismo milisegundo? RFC 9562 permite que las implementaciones incrementen un contador monótono en los bits aleatorios para garantizar el orden dentro del mismo milisegundo. La biblioteca uuid hace esto por defecto. El resultado: incluso si se generan 10.000 IDs en un milisegundo, siguen ordenándose correctamente.

El Problema de Fragmentación del Índice B-Tree en Detalle

Para entender por qué esto importa, considera lo que sucede con 10.000 inserciones por segundo con una clave primaria UUID v4 en una tabla con 100 millones de filas existentes:

  • Cada inserción genera una clave aleatoria de 128 bits que cae en una posición aleatoria entre las 100 millones de entradas existentes.
  • La base de datos debe cargar la página B-tree específica que contiene esa posición en el pool de búferes.
  • Con 100 millones de filas y páginas de 8 KB, el índice abarca aproximadamente 100.000 páginas. Cada segundo, se necesitan 10.000 páginas diferentes — muchas más de las que un shared_buffers típico de 8 GB puede mantener solo para este índice.
  • Cada fallo de caché resulta en una lectura de disco. Con 10.000 inserciones/seg, esto puede generar miles de lecturas de disco aleatorias por segundo, saturando incluso los SSDs en tablas grandes.

Con UUID v7, las 10.000 inserciones por segundo aterrizan en la página hoja más a la derecha (o en un pequeño número de páginas recientes). El pool de búferes solo necesita mantener esas pocas páginas activas. La tasa de aciertos de caché para escrituras se acerca al 100%. La amplificación de escritura cae drásticamente.

El mismo beneficio aplica a los escaneos de rango: WHERE created_at BETWEEN x AND y en una tabla v4 requiere un escaneo completo del índice o un índice de timestamp separado. En una tabla v7, la clave primaria en sí es el índice de timestamp — la consulta puede buscar directamente en el rango correcto.

Cómo Usar UUID v7 en PostgreSQL

-- Funciona de forma nativa con el tipo UUID en PostgreSQL 16+
CREATE TABLE events (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(), -- v4 (aleatorio)
  -- Cambiar a UUIDv7 mediante extensión o generación del lado de la aplicación
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Con UUIDv7: el timestamp ya está incrustado, por lo que
-- esta columna created_at separada suele ser redundante.

PostgreSQL 17 incluye uuidv7() de forma nativa. Para PostgreSQL 14–16, la extensión pg_uuidv7 proporciona la misma función. El tipo de datos UUID almacena tanto v4 como v7 de forma idéntica — 16 bytes, sin sobrecarga. La única diferencia es el patrón de bits que determina el orden de clasificación.

Una consecuencia útil: ya que el timestamp está incrustado, muchas tablas ya no necesitan una columna created_at separada para ordenar o mostrar. Puedes extraer el timestamp de un UUID v7 con una sola llamada de función. Esto reduce la complejidad del esquema y elimina una escritura por inserción.

UUID v4 vs v7 — Compensaciones

Propiedad UUID v4 UUID v7
Bits de aleatoriedad 122 74
Orden de clasificación Aleatorio Cronológico
Rendimiento INSERT B-tree Deficiente (fragmentación) Excelente (secuencial)
Filtración de timestamp Ninguna Sí (precisión en ms)
Estándar RFC RFC 4122 (2005), RFC 9562 (2024) RFC 9562 (2024)
Soporte de bibliotecas Universal Creciendo rápidamente (2024–2026)
created_at incrustado No

Guía de Migración — De v4 a v7

Migrar una tabla existente de claves primarias UUID v4 a v7 es una operación de varios pasos. La restricción clave: no puedes cambiar el valor de una clave primaria referenciada por claves foráneas sin actualizar también todas las tablas referenciadas. Planifica una ventana de mantenimiento o usa un enfoque de escritura dual.

-- 1. Añadir nueva columna
ALTER TABLE orders ADD COLUMN id_v7 UUID;

-- 2. Rellenar filas existentes (preservar el created_at original como fuente
--    del timestamp; usar una función de biblioteca UUIDv7 en tu app)
UPDATE orders SET id_v7 = generate_uuidv7(created_at);

-- 3. Verificar que no queden NULLs
SELECT COUNT(*) FROM orders WHERE id_v7 IS NULL;

-- 4. Intercambiar columnas (requiere una breve ventana de mantenimiento)
ALTER TABLE orders ALTER COLUMN id_v7 SET NOT NULL;
ALTER TABLE orders ALTER COLUMN id_v7 SET DEFAULT generate_uuidv7(now());
ALTER TABLE orders RENAME COLUMN id TO id_v4_old;
ALTER TABLE orders RENAME COLUMN id_v7 TO id;
ALTER TABLE orders ADD PRIMARY KEY (id);

Para las migraciones sin tiempo de inactividad en tablas de alto tráfico, el enfoque recomendado es:

  1. Añadir la nueva columna id_v7 con un valor predeterminado de servidor que genere UUIDs v7 a partir de ahora.
  2. Rellenar filas antiguas en lotes durante períodos de bajo tráfico, usando el timestamp created_at existente como semilla para la porción de timestamp de v7.
  3. Actualizar todas las columnas de clave foránea en las tablas referenciadas para que apunten a id_v7.
  4. Renombrar columnas y eliminar la restricción de clave primaria antigua durante una breve ventana de mantenimiento.

El paso de relleno es el más largo. Con 10.000 filas por lote y un sleep de 50 ms entre lotes, una tabla de 100 millones de filas tarda aproximadamente 8 horas. Empieza pronto.

Impacto en el Mundo Real

Varios equipos de ingeniería han publicado benchmarks comparando UUID v4 y v7 en conjuntos de datos de tamaño de producción. Los hallazgos consistentes:

  • Rendimiento de INSERT: mejora de 2–5x en tablas con 50M+ filas al cambiar de v4 a v7, con la ganancia aumentando a medida que crece el tamaño de la tabla.
  • Latencia de escritura p99: Cae de cientos de milisegundos (v4, bajo carga) a milisegundos de un solo dígito (v7) en el mismo hardware.
  • Tamaño de índice: Los índices B-tree en columnas v7 son un 15–30% más pequeños que los índices v4 equivalentes en los mismos datos porque la fragmentación deja menos páginas medio vacías.
  • Eficiencia del pool de búferes: La tasa de aciertos de shared_buffers para el índice de clave primaria pasa de ~40% (v4, tabla grande) a ~99% (v7), porque solo las páginas recientes necesitan mantenerse activas.

Las ganancias son insignificantes por debajo de aproximadamente 1 millón de filas. Si tu tabla se mantiene pequeña, quédate con v4 por simplicidad. Por encima de 10 millones de filas con cualquier tasa de INSERT significativa, v7 es el mejor valor predeterminado.

Cuándo Usar Cada Uno

Usa UUID v7 cuando:

  • Estás diseñando un nuevo esquema y la tabla crecerá mucho (10M+ filas).
  • La tabla tiene una tasa de INSERT alta — eventos, logs, pedidos, mensajes, notificaciones.
  • Quieres una clave primaria que también sirva como timestamp de creación (elimina una columna).
  • Estás en PostgreSQL 17, MySQL 8.0+ o un ORM moderno que soporta la generación v7.
  • Ordenar por ID es semánticamente equivalente a ordenar por tiempo de creación — lo que será para la mayoría de las tablas con muchas adiciones.

Usa UUID v4 cuando:

  • El identificador se expone a los usuarios y no debe revelar la hora de creación (códigos de invitación, enlaces de compartir, identificadores de facturación).
  • La tabla es pequeña y estable — ningún beneficio de rendimiento justifica el coste de migración.
  • Estás generando IDs en un contexto donde el timestamp de creación es sensible (registros de usuarios privados en un producto que compite en optics de crecimiento).
  • Necesitas algo como credencial o token de un solo uso — usa un generador de secretos dedicado, no ninguna versión de UUID.

Resumen

UUID v4 es aleatorio, privado y universalmente soportado. UUID v7 está ordenado por tiempo, es amigable con las bases de datos y ahora es estándar en las principales bibliotecas y bases de datos. Para nuevos esquemas con tablas grandes o de rápido crecimiento, v7 es el mejor valor predeterminado. Para IDs expuestos a usuarios donde la filtración del timestamp es una preocupación, v4 sigue siendo la elección correcta.

Genera ambas versiones al instante con el generador de UUID de Toova — sin cuenta necesaria. Para tokens más cortos, el generador de cadenas aleatorias produce IDs alfanuméricos de cualquier longitud.