Pular para o conteúdo

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:

text
ed65ec92-d01a-4853-adec-7e62a5108f40

Duas 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, a ou b.

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:

js
// navegadores e Node.js 19+
crypto.randomUUID(); // "ed65ec92-d01a-4853-adec-7e62a5108f40"
python
import uuid

str(uuid.uuid4())
sql
-- 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 uuid do PostgreSQL ocupa 16 bytes. O mesmo valor como text ocupa 36 bytes mais o overhead, e todo índice sobre ele cresce junto.
  • No MySQL, use BINARY(16) com UUID_TO_BIN() e BIN_TO_UUID() em vez de CHAR(36).
  • Padronize o formato. Guarde e compare sempre na mesma caixa, normalmente minúscula, para ED65EC92-... e ed65ec92-... 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:

text
01a1ab94-8600-7c3e-9f5b-3d2a8e6b1c40

Isso 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 text ou varchar quando 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