Resumo de UUID v4 e v7
Tanto UUID v4 quanto v7 são identificadores de 128 bits formatados como 32 caracteres hexadecimais agrupados como 8-4-4-4-12. A diferença esta no conteudo desses bits.
Um UUID v4 se parece com:
f47ac10b-58cc-4372-a567-0e02b2c3d479
^^^^
nibble de versão = 4
Um UUID v7 se parece com:
018e2b3c-d4a1-7f2e-b8c9-1234567890ab
^^^^^^^^^^^^^
48 bits timêstamp ms
^
nibble de versão = 7
Esta diferença estrutural tem implicações reais de desempenho para bancos de dados.
UUID v4: Estrutura
UUID v4 aloca 122 bits para aleatoriedade. Os 6 bits restantes são reservados:
- 4 bits codificam a versão (
0100= 4) - 2 bits codificam a variante (
10para RFC 4122)
xxxxxxxx-xxxx-4xxx-[89ab]xxx-xxxxxxxxxxxx
Os caracteres x são aleatórios. O 13o caractere e sempre 4. O 17o caractere e sempre um de 8, 9, a ou b.
Geração na maioria das linguagens é uma única chamada:
import uuid
str(uuid.uuid4())
# 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
crypto.randomUUID() // nativo desde Node 14.17 / todos os navegadores modernos
# 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
import "github.com/google/uuid"
uuid.New().String()
UUID v7: Estrutura
UUID v7, padronizado no RFC 9562 (maio de 2024), usa os primeiros 48 bits para um timêstamp Unix em milissegundos, seguido por bits de versão e dados aleatórios:
[48 bits timêstamp ms][4 bits versão = 7][12 bits aleatórios][2 bits variante][62 bits aleatórios]
O prefixo de timêstamp de 48 bits significa que UUIDs gerados no mêsmo milissegundo são quase identicos em seus bits mais significativos, e UUIDs gerados posteriormente são lexicograficamente maiores. Esta é a propriedade que torna o v7 amigavel para índices B-tree.
# Python 3.11+ tem uuid7 nativo na biblioteca padrão
import uuid
str(uuid.uuid7()) # Python 3.11+
// Nenhum suporte nativo ainda, use uma biblioteca
import { v7 as uuidv7 } from 'uuid';
uuidv7()
// '018e2b3c-d4a1-7f2e-b8c9-1234567890ab'
// Java não tem UUID v7 nativo, use java-uuid-generator
import com.fasterxml.uuid.Generators;
Generators.timeBasedEpochRandomGenerator().generate().toString();
Por que UUIDs Aleatorios Prejudicam o Desempenho do Banco de Dados
Indices B-tree (usados por PostgreSQL, MySQL, SQL Server é a maioria dos outros) mantem ordem sequencial. Quando você insere uma nova linha, o banco de dados encontra onde a nova chave pertence no índice ordenado é a coloca la.
Com UUID v4, novas chaves são distribuıdas uniformemente ao acasó em todo o espaco de 128 bits. A nova chave quase nunca pertence ao final do índice. O banco de dados deve:
- Ler a página de índice onde a chave pertence
- Se a página estiver cheia, dividi-la (alocando uma nova página e redistribuindo entradas)
- Atualizar nos do índice pai
Em altas taxas de inserção, issó causa divisões constantes de página, alta amplificação de escrita e baixa utilização de cache. Paginas de índice são removidas do pool de buffer assim que são escritas porque novas inserções quase nunca tocam a mêsma página duas vezes.
Com UUID v7, novas chaves são sempre maiores que todas as chaves anteriores (dentro do mêsmo milissegundo). As inserções vão para a página folha mais a direita da B-tree. As páginas são preenchidas sequencialmente, as divisões são raras e as páginas ativas permanecem no pool de buffer.
A diferença e mensuravel. Benchmarks no PostgreSQL com chaves primárias UUID v4 vs. v7 tipicamente mostram 20 a 50% melhor rendimento de inserção para v7 em tabelas grandes, com inchaco de índice significativamente menor.
Quando Usar Cada Versão
Use UUID v4 quando:
- Gerar tokens de seguranca, chaves de API, IDs de sessão ou tokens de redefinição de senha onde a imprevisibilidade importa. Os 122 bits completos de aleatoriedade significam que o ID não pode ser adivinhado ou sequenciado por um atacante.
- Os IDs não são usados como chaves primárias de banco de dados, ou a tabela e pequena o suficiente para que o desempenho do índice não importe.
- Você precisa de ampla compatibilidade. O v4 esta disponıvel em todas as principais linguagens e bibliotecas ha 20 anos.
Use UUID v7 quando:
- Usar UUIDs como chaves primárias de banco de dados, especialmente em tabelas que recebem altas taxas de inserção.
- Você precisa de IDs ordenaveis e ordenados por tempo que possam ser comparados cronologicamente sem uma coluna de timêstamp separada.
- Você esta projetando um sistema distribuıdo e quer reduzir a sobrecarga de coordenação. O prefixo de timêstamp fornece uma ordenação natural sem um gerador de sequencia central.
- Você esta construindo um log de eventos, trilha de auditoria ou qualquer sistema onde a ordem de inserção importe.
UUID v7 vs ULID
ULID (Identificador Universal Unico Lexicograficamente Ordenavel) resolve o mêsmo problema que UUID v7 (IDs ordenaveis e ordenados por tempo) com um formato diferente. ULIDs usam timêstamps de 48 bits em milissegundos e 80 bits de aleatoriedade, codificados como 26 caracteres Crockford Base32 (ex. 01ARZ3NDEKTSV4RRFFQ69G5FAV).
A diferença prática é a compatibilidade de formato. Se seu sistema espera strings em formato UUID (36 caracteres, hex, com hıfens), use UUID v7. Se você quer identificadores mais curtos é seguros para URL e controla o formato de ponta a ponta, ULID é uma escolha razoavel. Ambos são ordenados por tempo de geração; ambos evitam o problema B-tree.
UUID v7 tem a vantagem de ser um padrão IETF (RFC 9562), o que significa que o suporte do ecossistema esta crescendo rapidamente.
Identificando Versões de UUID na Pratica
Dada uma string UUID, a versão esta sempre na posição 14 (indexada em 0), o primeiro caractere do terceiro grupo:
xxxxxxxx-xxxx-Vxxx-xxxx-xxxxxxxxxxxx
^
posição 14, sempre o digito da versão
1→ v1 (baseado em tempo, endereco MAC)3→ v3 (baseado em nome, MD5)4→ v4 (aleatório)5→ v5 (baseado em nome, SHA-1)7→ v7 (ordenado por tempo, aleatório)
A variante esta na posição 19 (o primeiro caractere do quarto grupo): 8, 9, a ou b para UUIDs RFC 4122. Se você vir um 0 ou f na posição 14, o valor provavelmente não é um UUID padrão. Pode ser um UUID nulo, um UUID máximo ou um identificador não padrão com formato de UUID.