UUID v4 na prática — colisões, formatos e quando usar
O que significam as partes de um UUID, por que colisão no v4 não é preocupação real, como guardar UUIDs no banco e quando o UUID v7, ordenado por tempo, é melhor.
Por Felipe YamaokaPublicado em 5 de novembro de 20265 min de leitura
Um ID que ninguém precisa distribuir
Um ID auto-incremento é simples até você precisar do ID antes de salvar, de IDs criados em vários serviços ao mesmo tempo ou de IDs que não revelem quantos pedidos a loja teve ontem. É aí que entram os UUIDs: identificadores de 128 bits que qualquer máquina gera sozinha, sem combinar com ninguém, e que na prática são únicos.
Este guia explica como ler um UUID, o quão único o v4 realmente é, como gerar e guardar do jeito certo e quando outra versão encaixa melhor.
Anatomia de um UUID
Um UUID normalmente é escrito como 32 dígitos hexadecimais em cinco grupos separados por hífen, 36 caracteres no total:
ed65ec92-d01a-4853-adec-7e62a5108f40Duas posições não são aleatórias, e elas permitem reconhecer o formato de cara:
- A versão é o primeiro dígito do terceiro grupo. Aqui é
4, então este é um UUID versão 4. - A variante é o primeiro dígito do quarto grupo. Nos UUIDs padrão ele é sempre
8,9,aoub.
Todo o resto de um UUID v4 é aleatório: 122 bits de 128. As letras podem vir em minúscula ou maiúscula, e as duas representam o mesmo valor; a especificação recomenda minúsculas na saída e aceitar qualquer uma na entrada. Alguns sistemas tiram os hífens e guardam só os 32 dígitos.
A especificação atual é a RFC 9562, publicada em 2024. Ela substituiu a antiga RFC 4122 e acrescentou versões novas, entre elas a v7.
Qual a chance de uma colisão?
Com 122 bits aleatórios existem cerca de 5,3 × 10 elevado a 36 UUIDs possíveis. A pergunta que importa não é "dois podem ser iguais?", e sim "quantos eu preciso gerar até uma repetição ficar provável?", que é o famoso problema do aniversário.
Alguns números, supondo uma boa fonte de aleatoriedade:
- Gerando 1 milhão de UUIDs, a chance de alguma repetição é de cerca de 1 em 10 elevado a 25.
- Gerando 1 bilhão, cerca de 1 em 10 elevado a 19.
- Para chegar a 50% de chance de uma única colisão, seriam necessários uns 2,7 × 10 elevado a 18 UUIDs. A um bilhão por segundo, isso leva cerca de 86 anos.
Na prática, quem causa UUID repetido são bugs, e não a matemática: um gerador aleatório fraco, uma fixture de teste copiada para produção ou um valor reaproveitado sem querer. Mantenha uma constraint de unicidade no banco mesmo assim; ela não custa nada e pega esses bugs.
Gerando UUIDs do jeito certo
A aleatoriedade precisa vir de uma fonte criptograficamente segura. Toda plataforma moderna já tem uma:
// navegadores e Node.js 19+
crypto.randomUUID(); // "ed65ec92-d01a-4853-adec-7e62a5108f40"import uuid
str(uuid.uuid4())-- PostgreSQL 13+
SELECT gen_random_uuid();Evite geradores escritos na mão com Math.random(). Ele não foi feito para garantir unicidade, e alguns motores já produziram sequências repetidas. No navegador, o crypto.randomUUID() só existe em contexto seguro, ou seja, em páginas servidas por HTTPS ou em localhost.
O Gerador de UUID deste site usa o crypto.randomUUID() direto no seu navegador. Ele gera até 1.000 de uma vez, em maiúsculas ou sem hífens, o que ajuda a montar dados de teste, seeds e fixtures.
Guardando UUIDs no banco
Como você guarda o UUID importa mais do que como você gera:
- Use o tipo nativo quando existir. O tipo
uuiddo PostgreSQL ocupa 16 bytes. O mesmo valor comotextocupa 36 bytes mais o overhead, e todo índice sobre ele cresce junto. - No MySQL, use
BINARY(16)comUUID_TO_BIN()eBIN_TO_UUID()em vez deCHAR(36). - Padronize o formato. Guarde e compare sempre na mesma caixa, normalmente minúscula, para
ED65EC92-...eed65ec92-...não virarem chaves diferentes em sistemas que comparam texto.
Existe também a questão de desempenho. UUIDs aleatórios se espalham por todo o espaço de chaves, então cada insert cai num lugar aleatório do índice B-tree. Em tabelas grandes, isso gera mais divisões de página e pior aproveitamento de cache do que IDs sequenciais. Para muitas aplicações a diferença é desprezível. Para tabelas com muita escrita e centenas de milhões de linhas, vale considerar um UUID ordenado por tempo.
UUID v7: UUIDs ordenados por tempo
A versão 7, introduzida na RFC 9562, coloca um timestamp Unix em milissegundos nos primeiros 48 bits e completa o resto com dados aleatórios. O resultado continua único sem coordenação, mas UUIDs criados depois ficam ordenados depois dos anteriores:
01a1ab94-8600-7c3e-9f5b-3d2a8e6b1c40Isso traz a localidade de índice de um ID sequencial e permite ordenar registros pela data de criação usando só a chave. A contrapartida é que o horário de criação fica visível para quem vê o ID, o que pode importar em identificadores públicos. O suporte está crescendo: o PostgreSQL 18 traz uuidv7(), e existem bibliotecas para a maioria das linguagens.
Uma regra razoável: use v4 quando o ID for público ou a ordem não importar, e v7 para chaves primárias de tabelas grandes, em que o desempenho de insert e a ordenação por tempo fazem diferença.
Quando não usar UUID
- Quando pessoas precisam ler ou digitar o ID. Um código curto, como o número do pedido, é bem mais amigável num atendimento por telefone ou WhatsApp.
- Como segredo. Um UUID v4 é difícil de adivinhar, mas é um identificador, e não um token. Para links de redefinição de senha ou chaves de API, gere um token aleatório próprio e guarde só o hash dele.
- Quando uma chave sequencial pequena resolve. Uma tabela interna de referência com algumas centenas de linhas não precisa de chaves de 16 bytes.
Erros comuns
- Guardar UUIDs como
textouvarcharquando existe tipo nativo. - Misturar maiúsculas e minúsculas num sistema que compara strings.
- Gerar UUIDs com
Math.random()ou outra fonte não criptográfica. - Tirar a constraint de unicidade porque "UUID nunca colide".
- Usar UUID como token de acesso.
- Expor UUIDs v7 publicamente sem considerar que eles revelam o horário de criação.
Resumo
Um UUID v4 tem 122 bits aleatórios num formato padrão de 36 caracteres, e com uma fonte aleatória segura colisão não é uma preocupação prática. Gere com a função nativa da plataforma, guarde num tipo nativo de 16 bytes e mantenha a constraint de unicidade. Quando o desempenho de insert ou a ordenação por tempo importarem, considere o UUID v7; quando o valor precisar ficar em segredo, use um token de verdade.
Leia também
Base64 não é criptografia — o que é, quando usar e a variante URL-safe
Como o Base64 transforma bytes em texto, por que ele não esconde nada, como tratar acentos e emoji do jeito certo e quando usar o alfabeto URL-safe.
5 de novembro de 20265 min de leituraFerramenta: Codificador/Decodificador Base64
A regex que trava o navegador — backtracking catastrófico explicado
Por que uma expressão regular curta pode levar segundos ou travar a página, como reconhecer quantificadores aninhados e como reescrever o padrão.
5 de novembro de 20266 min de leituraFerramenta: Testador de Regex