Quão bom é o UUID.randomUUID do Java?

311

Eu sei que UUIDs randomizados têm uma probabilidade muito, muito, muito baixa de colisão na teoria, mas estou me perguntando, na prática, quão bom randomUUID()é o Java em termos de não ter colisão? Alguém tem alguma experiência para compartilhar?

Alvin
fonte
10
Na minha experiência, eu nunca vi uma colisão ;-)
Thilo
4
Os algoritmos são especificados em RFC1422: ietf.org/rfc/rfc4122.txt
skaffman
8
@skaffman: o RFC não diz absolutamente nada sobre o algoritmo usado para gerar os dígitos aleatórios.
precisa
4
Como essa é uma pergunta mais aberta, acho que não vou marcar nenhuma resposta como a resposta correta; em vez disso, vou dar um voto a cada uma das respostas que eu acho que é bom :)
Alvin
5
Da wikipedia: ... Em outras palavras, somente após gerar 1 bilhão de UUIDs a cada segundo pelos próximos 100 anos, a probabilidade de criar apenas uma duplicata seria de cerca de 50%.
MaVRoSCy

Respostas:

168

Usos de UUID java.security.SecureRandom, que deveriam ser "criptograficamente fortes". Embora a implementação real não seja especificada e possa variar entre JVMs (o que significa que quaisquer declarações concretas feitas são válidas apenas para uma JVM específica), ela exige que a saída seja aprovada em um teste estatístico de gerador de números aleatórios.

É sempre possível que uma implementação contenha erros sutis que arruinam tudo isso (consulte o bug de geração de chaves do OpenSSH), mas não acho que exista qualquer razão concreta para se preocupar com a aleatoriedade do Java UUIDs.

Michael Borgwardt
fonte
34
"É sempre possível que uma implementação contenha erros sutis ..." - Ou (usando um chapéu de papel alumínio) ... falhas sutis deliberadas. <:-)
Stephen C
25
A força criptográfica é completamente irrelevante para a questão das colisões.
osa
14
@osa: não produzir colisões (mais do que o esperado de uma aleatoriedade perfeita) é praticamente o requisito de qualidade mais baixo para um RNG, enquanto a força criptográfica é a mais alta. Em outras palavras, um RNG criptograficamente forte definitivamente não produzirá mais colisões do que o esperado.
Michael Borgwardt
3
No entanto, pode ser útil observar que, se você executar uma JVM criando UUIDs em blogs.vmware.com/cto/… , provavelmente terá muitas colisões. Todos os RNGs de software são PRNGs e, em última análise, são tão bons quanto sua fonte de entropia; dois PRNGs que são propagados de forma idêntica também se comportam de forma idêntica, e isso pode ocorrer surpreendentemente com configurações consistentes e duplicadas exatamente exatas do servidor e procedimentos de inicialização.
User508633
@ user508633: Na verdade, eu esperaria obter uma taxa de colisão de 100% nesse caso específico, mas é um caso muito específico que vai muito além de "configurações consistentes e com duplicação exata do servidor e procedimentos de inicialização". Tenho certeza de que você não obteria maiores taxas de colisão se apenas clonasse uma VM e a executasse normalmente. A auto-propagação do SecureRandom tenta bastante obter alguma entropia real, a ponto de impedir a execução, se não encontrar: seancassidy.me/wiggle-the-mouse-to-fix-the-test.html
Michael Borgwardt
114

A Wikipedia tem uma resposta muito boa http://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions

o número de UUIDs da versão aleatória 4 que precisam ser gerados para ter uma probabilidade de 50% de pelo menos uma colisão é de 2,71 quintilhões, calculado da seguinte forma:

...

Esse número equivale a gerar 1 bilhão de UUIDs por segundo por cerca de 85 anos, e um arquivo contendo muitos UUIDs, com 16 bytes por UUID, seria cerca de 45 exabytes, muitas vezes maior que os maiores bancos de dados existentes atualmente, que estão em a ordem de centenas de petabytes.

...

Portanto, para que haja uma chance em um bilhão de duplicação, 103 U $ 3 trilhões de UUIDs da versão 4 devem ser gerados.

sheki
fonte
56
Eu também citaria nessa página: "A probabilidade de uma duplicata seria de cerca de 50% se todas as pessoas na terra possuírem 600 milhões de UUIDs".
Jeff Axelrod
24
Isso é verdade apenas para a aleatoriedade verdadeira, não para números pseudo-aleatórios como UUIDs de javas.
Markus
9
@ Markus: completamente errado. A probabilidade de colisões para bons RNGs pseudo-aleatórios, especialmente os criptograficamente fortes, não é diferente da aleatoriedade "verdadeira".
22613 Michael Borgwardt
6
@ Eric - Eu acho que o ônus está em você fazer backup de sua afirmação. FWIW, os únicos cenários em que posso pensar em onde os UUIDs do tipo 4 colidiriam com mais frequência do que a teoria das probabilidades diz que deveriam ser: 1) uma fonte ruim de números aleatórios criptográficos ou 2) uma biblioteca de UUID comprometida.
Stephen C
13
Isso não responde à pergunta. A questão é sobre a qualidade da aleatoriedade em Java UUID.randomUUID(), não sobre as chances teóricas para um determinado gerador de números aleatórios perfeito.
precisa saber é o seguinte
69

Alguém tem alguma experiência para compartilhar?

Existem 2^122valores possíveis para um UUID do tipo 4. (A especificação diz que você perde 2 bits para o tipo e mais 4 bits para um número de versão.)

Supondo que você gerasse 1 milhão de UUIDs aleatórios por segundo, as chances de uma duplicação ocorrerem durante a sua vida seriam muito pequenas. E para detectar a duplicata, você teria que resolver o problema de comparar 1 milhão de novos UUIDs por segundo com todos os UUIDs que você gerou anteriormente 1 !

As chances de alguém ter experimentado (ou seja, de fato notado ) uma duplicata na vida real são ainda menores do que as surpreendentemente pequenas ... devido à dificuldade prática de procurar colisões.

Agora, é claro, você normalmente estará usando um gerador de números pseudo-aleatórios, não uma fonte de números verdadeiramente aleatórios. Mas acho que podemos ter certeza de que, se você estiver usando um provedor confiável para seus números aleatórios de força criptográfica, será uma força criptográfica, e a probabilidade de repetições será a mesma que para um gerador de números aleatórios ideal (não tendencioso) .

No entanto, se você usar uma JVM com um gerador de números cripto-aleatórios "quebrado", todas as apostas serão desativadas. (E isso pode incluir algumas das soluções alternativas para problemas de "falta de entropia" em alguns sistemas. Ou a possibilidade de alguém ter mexido com o seu JRE, no seu sistema ou no upstream.)


1 - Supondo que você tenha usado "algum tipo de btree binário", como proposto por um comentarista anônimo, cada UUID precisará de O(NlogN)bits de memória RAM para representar NUUIDs distintos, assumindo baixa densidade e distribuição aleatória dos bits. Agora multiplique isso por 1.000.000 e o número de segundos pelos quais você executará o experimento. Não acho que seja prático pelo tempo necessário para testar colisões de um RNG de alta qualidade. Nem mesmo com representações inteligentes (hipotéticas).

Stephen C
fonte
4
"(E para detectar a duplicata, você teria que resolver o problema de comparar 1 milhão de novos UUIDs por segundo com todos os UUIDs que você gerou anteriormente!)" - essa parte é relativamente direta, desde que você tenha armazenado seus uuids em alguns tipo de estrutura de árvore binária, seria apenas uma descida de árvore por novo uuid. Você não precisaria compará-lo individualmente com todos os uuids gerados anteriormente.
User467257
20

Eu não sou um especialista, mas eu diria que pessoas inteligentes o suficiente olharam para o gerador de números aleatórios do Java ao longo dos anos. Portanto, eu também consideraria que UUIDs aleatórios são bons. Portanto, você realmente deve ter a probabilidade de colisão teórica (que é de cerca de 1: 3 × 10 ^ 38 para todos os UUIDs possíveis. Alguém sabe como isso muda apenas para UUIDs aleatórios? É isso 1/(16*4)?)

Pela minha experiência prática, nunca vi colisões até agora. Provavelmente terei uma barba surpreendentemente longa no dia em que receber minha primeira;)

sfussenegger
fonte
10
Da wikipedia: ... Em outras palavras, somente após gerar 1 bilhão de UUIDs a cada segundo pelos próximos 100 anos, a probabilidade de criar apenas uma duplicata seria de cerca de 50%.
MaVRoSCy
1
Na verdade wikipedia diz que é para os próximos 85 anos ... Eu digo não conte com isso, alguém em algum lugar tem gerado o mesmo UUID como você
smac89
12

Em um ex-empregador, tínhamos uma coluna única que continha um uuid aleatório. Tivemos uma colisão na primeira semana após a implantação. Claro, as chances são baixas, mas não são zero. É por isso que o Log4j 2 contém UuidUtil.getTimeBasedUuid. Ele gerará um UUID exclusivo por 8.925 anos, desde que você não gere mais de 10.000 UUIDs / milissegundo em um único servidor.

rgoers
fonte
2
Sim. Mas a questão é perguntar sobre UUIDs aleatórios (ou seja, do tipo 4).
Stephen C
1
Ele está perguntando sobre a probabilidade de uma colisão. A implicação é que ele quer ter certeza de evitá-los.
rgoers
1
(A colisão foi provavelmente devido a uma fonte quebrada de aleatoriedade para a semeadura das PRNGs Pensamento eu acho que era possível que era devido ao puro acaso..)
Stephen C
9

O esquema de geração original para UUIDs era concatenar a versão UUID com o endereço MAC do computador que está gerando o UUID e com o número de intervalos de 100 nanossegundos desde a adoção do calendário gregoriano no Ocidente. Ao representar um único ponto no espaço (o computador) e no tempo (o número de intervalos), a chance de uma colisão nos valores é efetivamente nula.

Alex2Ustas
fonte
1
Essa explicação me deixa otimista em não ver colisões na prática. Você pode apontar para qualquer referência a essa declaração (algum código-fonte seria ainda melhor)?
Dragan Marjanović
Encontrei isso nas especificações ietf.org/rfc/rfc4122.txt . No entanto, seria ótimo ver a implementação.
Dragan Marjanović
1
Esse esquema não é o que Java implementa, no entanto. O Java implementa o UUID do tipo 4, que é totalmente aleatório e não inclui o horário ou endereço MAC. Aliás, como agora existem muitos dispositivos físicos e virtuais nos quais você pode escolher o seu endereço MAC, o algoritmo original não garante exclusividade.
Søren Boisen
8

Muitas das respostas discutem quantos UUIDs teriam que ser gerados para atingir 50% de chance de uma colisão. Mas uma chance de colisão de 50%, 25% ou até 1% não vale nada para um aplicativo em que a colisão deve ser (virtualmente) impossível.

Os programadores rotineiramente descartam como "impossíveis" outros eventos que podem e ocorrem?

Quando gravamos dados em um disco ou memória e os lemos novamente, assumimos que os dados estão corretos. Contamos com a correção de erros do dispositivo para detectar qualquer corrupção. Mas a chance de erros não detectados é de 2 a 50 .

Não faria sentido aplicar um padrão semelhante a UUIDs aleatórios? Se o fizer, verá que uma colisão "impossível" é possível em uma coleção de cerca de 100 bilhões de UUIDs aleatórios (2 36,5 ).

Esse é um número astronômico, mas aplicações como cobrança detalhada em um sistema nacional de saúde ou registro de dados de sensores de alta frequência em uma grande variedade de dispositivos podem definitivamente atingir esses limites. Se você estiver escrevendo o próximo Guia do Mochileiro das Galáxias, não tente atribuir UUIDs a cada artigo!

erickson
fonte
Como ponto de comparação, a chance de ganhar um jackpot da Powerball é de 1 em 300 milhões, mas as vendas de 10 a 20 milhões de ingressos são típicas. O ponto é que muitas pessoas definem "impossível" como algo menos que uma chance em centenas de milhões.
precisa
4

Como a maioria das respostas se concentrou na teoria, acho que posso acrescentar algo à discussão dando um teste prático que fiz. No meu banco de dados, tenho cerca de 4,5 milhões de UUIDs gerados usando o Java 8 UUID.randomUUID (). Os seguintes são apenas alguns que eu descobri:

c0f55f62 -b990-47bc-8caa-f42313669948

c0f55f62 -e81e-4253-8299-00b4322829d5

c0f55f62 -4979-4e87-8cd9-1c556894e2bb


b9ea2498-fb32-40ef-91ef-0ba 00060fe64

be87a209-2114-45b3-9d5a-86d 00060fe64


4a8a74a6-e972-4069-b480-b dea1177b21f

12fb4958-bee2-4c89-8cf8-e dea1177b21f

Se fosse realmente aleatório, a probabilidade de ter esse tipo de UUIDs semelhantes seria consideravelmente baixa (veja editar), já que estamos considerando apenas 4,5 milhões de entradas. Portanto, embora essa função seja boa, em termos de não ter colisões, para mim não parece tão bom quanto seria em teoria.

Editar :

Muitas pessoas parecem não entender essa resposta, então vou esclarecer meu argumento: sei que as semelhanças são "pequenas" e estão longe de ser uma colisão completa. No entanto, eu só queria comparar o UUID.randomUUID () do Java com um verdadeiro gerador de números aleatórios, que é a questão real.

Em um gerador de números aleatórios verdadeiro, a probabilidade de ocorrência do último caso seria em torno de 0,007%. Portanto, acho que minha conclusão permanece.

A fórmula é explicada neste artigo da wiki en.wikipedia.org/wiki/Birthday_problem

André Pinheiro
fonte
6
Isso não é verdade. Esse tipo de similaridade surgiria mesmo com um verdadeiro gerador de números aleatórios em 4,5 milhões de uuids. As semelhanças entre os UUIDs que você forneceu são pequenas e distantes, oh, tão distantes de uma colisão completa.
user3711864
Concordo plenamente com você que as semelhanças são "pequenas" e longe de uma colisão total. No entanto, eu só queria comparar o UUID.randomUUID () do Java com um verdadeiro gerador de números aleatórios (eis a questão). Com alguns cálculos, podemos ver que, em um gerador de números aleatórios verdadeiro, a probabilidade de ocorrência do último caso seria em torno de 1-e ^ (- 4500000 ^ 2 / (2 * 36 ^ 11)) = 0,007% = 1 em um 13k. Eu teria que ter muita sorte :)
André Pinheiro
1
Com 4,5 milhões de itens e uma chance de 1 em 13 mil, uma colisão parcial como essa não seria esperada 346 vezes?
Ben Lee
Não, @BenLee, calculei a probabilidade desse evento acontecer, considerando que temos 4,5 milhões de itens. Não é uma chance de 1 em 13 mil de ocorrência para cada item. A fórmula que usei pode ser encontrada neste artigo da wiki en.wikipedia.org/wiki/Birthday_problem
André Pinheiro
2
Qual era a sua expectativa? Semelhante não é o mesmo, não é?
Koray Tugay
3

Eu jogo na loteria no ano passado e nunca ganhei ... mas parece que a loteria tem vencedores ...

doc: http://tools.ietf.org/html/rfc4122

Tipo 1: não implementado. colisão é possível se o uuid for gerado no mesmo momento. O impl pode ser sincronizado artificialmente para contornar esse problema.

Tipo 2: nunca vê uma implementação.

Tipo 3: md5 hash: possível colisão (128 bits-2 bytes técnicos)

Tipo 4: aleatório: possível colisão (como loteria). observe que o implemento jdk6 não usa um aleatório seguro "verdadeiro" porque o algoritmo PRNG não é escolhido pelo desenvolvedor e você pode forçar o sistema a usar um PRNG "ruim". Portanto, seu UUID é previsível.

Tipo 5: sha1 hash: não implementado: é possível colisão (160 bits-2 bytes técnicos)

Giher
fonte
4
A probabilidade de ganhar na loteria é talvez uma entre 10 ou 100 milhões (10 ^ 7 ou 10 ^ 8) ou algo assim. A probabilidade de uma colisão com um número aleatório de 128 bits é de 3,4 * 10 ^ 28. Me dê um bilhete de loteria a qualquer momento!
Stephen C
0

Temos usado o UUID aleatório do Java em nosso aplicativo há mais de um ano e isso muito extensivamente. Mas nunca encontramos uma colisão.

Afsar
fonte