Pular para o conteúdo
Obtani
Desenvolvimento Web

UUID é Único Mesmo? A Conta da Probabilidade de Colisão

5 min de leitura

Quer o número agora? Gere UUIDs nas versões 4 e 7, em lote, no formato que você precisa e com cópia num clique.

Gerador de UUID

Quase todo texto sobre UUID abre com a mesma promessa: "identificador universalmente único". A própria RFC 9562, a norma em vigor desde maio de 2024, é mais honesta que isso. Ela diz, na seção 6.8, que a verdadeira unicidade global é impossível de garantir sem um esquema de conhecimento compartilhado — e que, ainda assim, esse esquema não é necessário para fins práticos.

Traduzindo: nenhum gerador de UUID consulta nada. O seu programa não sabe o que o programa do vizinho gerou, nem o que ele mesmo gerou ontem. A unicidade não é garantida; ela é improvável a ponto de poder ser ignorada. A diferença entre as duas afirmações é o assunto deste texto.

Quantos bits, afinal

Um UUID tem 128 bits. Mas nem todos são sorteados. Na versão 4, seis deles são fixos e servem para dizer o que aquilo é:

  • 4 bits de versão, no 13º dígito hexadecimal — sempre 4 num UUID v4;
  • 2 bits de variante, nos dois primeiros bits do 17º dígito — a RFC exige que sejam 1 e 0, e é por isso que esse dígito é sempre 8, 9, a ou b.

Sobram 122 bits de sorteio. É esse o número que entra na conta — não 128. A diferença parece pequena, mas 2^128 é 64 vezes maior que 2^122, e quem usa o número errado está sendo otimista por um fator considerável.

O espaço de 122 bits tem, em números redondos, 5,3 undecilhões de valores possíveis — um 53 seguido de 35 zeros.

A conta certa é a do paradoxo do aniversário

Aqui está o erro mais comum. A pergunta que interessa não é "qual a chance de um UUID novo bater com um UUID específico que eu já tenho". Essa é fácil e é praticamente zero: 1 em 5,3 undecilhões.

A pergunta certa é: qual a chance de, entre todos os UUIDs que eu já gerei, existir algum par repetido. E essa cresce muito mais rápido, porque o número de pares cresce com o quadrado da quantidade. É o mesmo raciocínio do paradoxo do aniversário, em que bastam 23 pessoas numa sala para a chance de duas fazerem aniversário no mesmo dia passar de 50%.

Aplicada aos 122 bits, a aproximação padrão dá o seguinte:

UUIDs geradosChance de existir ao menos uma repetição
1 milhão1 em 10 setilhões (0,000000000000000000000009%)
1 bilhão1 em 10 quintilhões
1 trilhão1 em 10 trilhões
1 quatrilhão1 em 10 milhões
2,7 quintilhões50%

A última linha é a que costuma circular em formato mais palatável: gerando um bilhão de UUIDs por segundo, sem parar, levaria cerca de 86 anos para chegar a 50% de chance de uma única repetição.

Para dar escala ao "1 trilhão": se o seu sistema criasse mil registros por segundo, ininterruptamente, levaria mais de trinta anos para chegar lá. E a chance de colisão, nesse ponto, ainda seria de 1 em 10 trilhões — muito menor que a de um erro de disco corromper um byte, que ninguém costuma perder o sono por causa disso.

Onde a conta deixa de valer

O número acima só existe se o sorteio for realmente aleatório. É aí que mora o risco de verdade — e ele não é matemático, é de implementação:

  • Gerador fraco. Se o UUID sair de um Math.random() ou de um gerador semeado com o relógio, a quantidade real de valores possíveis despenca. Duas máquinas ligadas ao mesmo tempo, com o mesmo relógio, podem produzir a mesma sequência. A RFC recomenda explicitamente um gerador criptograficamente seguro; na web, isso é o crypto.getRandomValues.
  • Estado duplicado. Contêineres clonados a partir de uma imagem que já tinha o gerador semeado, ou máquinas virtuais restauradas de um mesmo snapshot, podem repetir a sequência inteira. É uma classe de falha real, e já causou incidentes documentados.
  • Truncamento. Guardar "só os 8 primeiros caracteres para ficar mais curto" reduz o espaço de 122 bits para 32. Aí a chance de colisão deixa de ser teórica: com 100 mil valores truncados, a probabilidade passa de 50%.

Em outras palavras: a matemática está do seu lado, desde que a implementação esteja também.

E se eu realmente não puder ter uma colisão?

Então você não deve confiar no formato — deve confiar no banco de dados.

Uma restrição de unicidade na coluna transforma uma colisão, que seria uma corrupção silenciosa de dados, num erro de inserção que o seu código trata. O custo é praticamente zero (o índice da chave primária já faz isso) e a garantia passa a ser real, não probabilística.

É o mesmo princípio de qualquer sistema bem construído: a improbabilidade é uma boa otimização, mas não substitui uma verificação.

A parte que mais gente erra: UUID não é segredo

Há uma confusão frequente entre "difícil de repetir" e "difícil de adivinhar". São propriedades diferentes, e o UUID entrega bem só a primeira.

Os 122 bits de entropia de um v4 são, no papel, mais do que uma senha forte tem. O problema não é a entropia: é por onde o valor circula. Um UUID aparece na barra de endereços, no histórico do navegador, no cabeçalho Referer enviado para outros sites, nos logs do servidor, nos logs do proxy, na tela compartilhada de uma reunião. Uma senha não percorre nenhum desses caminhos.

A RFC 9562 é direta sobre isso na seção de considerações de segurança: implementações não devem tratar UUIDs como identificadores de segurança que concedem acesso mediante posse. Ou seja: nada de usar UUID como token de sessão, chave de API, senha de convite ou "link secreto" que dá acesso a um documento.

E a versão 7 é pior nesse papel, porque metade dela é o horário de criação — informação previsível por construção.

Para gerar identificadores, use o gerador de UUID. Para gerar segredos, use o gerador de senhas. São problemas diferentes, e a ferramenta certa para cada um é diferente.

Leia também