So che gli UUID randomizzati hanno una probabilità molto, molto, molto bassa di collisione in teoria, ma mi chiedo, in pratica, quanto è buono Java randomUUID()
in termini di non avere collisioni? Qualcuno ha qualche esperienza da condividere?
So che gli UUID randomizzati hanno una probabilità molto, molto, molto bassa di collisione in teoria, ma mi chiedo, in pratica, quanto è buono Java randomUUID()
in termini di non avere collisioni? Qualcuno ha qualche esperienza da condividere?
Risposte:
UUID utilizza java.security.SecureRandom
, che dovrebbe essere "crittograficamente forte". Sebbene l'implementazione effettiva non sia specificata e possa variare tra le JVM (il che significa che tutte le dichiarazioni concrete fatte sono valide solo per una JVM specifica), richiede che l'output debba superare un test statistico di generazione di numeri casuali.
È sempre possibile che un'implementazione contenga dei bug sottili che rovinano tutto questo (vedi bug di generazione delle chiavi di OpenSSH) ma non penso che ci siano ragioni concrete per preoccuparsi della casualità degli UUID Java.
Wikipedia ha un'ottima risposta http://en.wikipedia.org/wiki/Universally_unique_identifier#Collisions
il numero di UUID casuali della versione 4 che devono essere generati per avere una probabilità del 50% di almeno una collisione è di 2,71 quintilioni, calcolato come segue:
...
Questo numero equivale a generare 1 miliardo di UUID al secondo per circa 85 anni e un file contenente così tanti UUID, a 16 byte per UUID, sarebbe di circa 45 exabyte, molte volte più grande dei più grandi database attualmente esistenti, che sono attivi l'ordine di centinaia di petabyte.
...
Pertanto, affinché ci sia una possibilità su un miliardo di duplicazioni, è necessario generare 103 trilioni di UUID versione 4.
UUID.randomUUID()
, non le possibilità teoriche per un dato generatore di numeri casuali perfetto.
Qualcuno ha qualche esperienza da condividere?
Esistono 2^122
valori possibili per un UUID di tipo 4. (Le specifiche indicano che si perdono 2 bit per il tipo e altri 4 bit per un numero di versione.)
Supponendo che tu dovessi generare 1 milione di UUID casuali al secondo, le probabilità che si verifichi un duplicato nella tua vita sarebbero minime. E per rilevare il duplicato, dovresti risolvere il problema di confrontare 1 milione di nuovi UUID al secondo con tutti gli UUID che hai precedentemente generato 1 !
Le probabilità che qualcuno abbia sperimentato (cioè notato in realtà ) un duplicato nella vita reale sono persino più piccole di quelle sparitamente piccole ... a causa della difficoltà pratica di cercare collisioni.
Ora, naturalmente, in genere utilizzerai un generatore di numeri pseudo-casuale, non una fonte di numeri veramente casuali. Ma penso che possiamo essere sicuri che se stai usando un fornitore di credito per i tuoi numeri casuali di forza crittografica, allora sarà forza crittografica e la probabilità di ripetizioni sarà la stessa di un generatore di numeri casuali ideale (non di parte) .
Tuttavia, se dovessi usare una JVM con un generatore di numeri crittografici "rotti", tutte le scommesse sono disattivate. (E ciò potrebbe includere alcune soluzioni alternative per problemi di "carenza di entropia" su alcuni sistemi. O la possibilità che qualcuno abbia armeggiato con il tuo JRE, sia sul tuo sistema che a monte.)
1 - Supponendo che tu abbia usato "una sorta di btree binario" come proposto da un commentatore anonimo, ogni UUID avrà bisogno di O(NlogN)
bit di memoria RAM per rappresentare N
UUID distinti assumendo bassa densità e distribuzione casuale dei bit. Ora moltiplicalo per 1.000.000 e il numero di secondi per cui eseguirai l'esperimento. Non penso che sia pratico per il tempo necessario per testare le collisioni di un RNG di alta qualità. Nemmeno con rappresentazioni (ipotetiche) intelligenti.
Non sono un esperto, ma presumo che nel corso degli anni abbastanza persone intelligenti abbiano guardato il generatore di numeri casuali di Java. Quindi, suppongo anche che gli UUID casuali siano buoni. Quindi dovresti davvero avere la probabilità teorica di collisione (che è circa 1: 3 × 10 ^ 38 per tutti i possibili UUID. Qualcuno sa come questo cambia solo per gli UUID casuali? È 1/(16*4)
di quanto sopra?)
Dalla mia esperienza pratica, non ho mai visto collisioni finora. Probabilmente mi sarò fatto crescere una barba sorprendentemente lunga il giorno in cui avrò il mio primo;)
In un ex datore di lavoro avevamo una colonna unica che conteneva un uuid casuale. Abbiamo avuto una collisione la prima settimana dopo che è stato schierato. Certo, le probabilità sono basse ma non sono zero. Ecco perché Log4j 2 contiene UuidUtil.getTimeBasedUuid. Genererà un UUID unico per 8.925 anni, purché non si generino più di 10.000 UUID / millisecondi su un singolo server.
Lo schema di generazione originale per gli UUID era di concatenare la versione UUID con l'indirizzo MAC del computer che sta generando l'UUID e con il numero di intervalli di 100 nanosecondi dall'adozione del calendario gregoriano in Occidente. Rappresentando un singolo punto nello spazio (il computer) e il tempo (il numero di intervalli), la possibilità di una collisione di valori è effettivamente nulla.
Molte delle risposte discutono su quanti UUID dovrebbero essere generati per raggiungere una probabilità del 50% di una collisione. Ma una probabilità di collisione del 50%, 25% o addirittura dell'1% è inutile per un'applicazione in cui la collisione deve essere (praticamente) impossibile.
I programmatori considerano abitualmente come "impossibili" altri eventi che possono e si verificano?
Quando scriviamo i dati su un disco o su una memoria e li rileggiamo di nuovo, diamo per scontato che i dati siano corretti. Facciamo affidamento sulla correzione degli errori del dispositivo per rilevare eventuali danni. Ma la possibilità di errori non rilevati è in realtà intorno ai 2-50 .
Non avrebbe senso applicare uno standard simile agli UUID casuali? Se lo fai, scoprirai che una collisione "impossibile" è possibile in una raccolta di circa 100 miliardi di UUID casuali (2 36,5 ).
Questo è un numero astronomico, ma applicazioni come la fatturazione dettagliata in un sistema sanitario nazionale o la registrazione dei dati dei sensori ad alta frequenza su una vasta gamma di dispositivi potrebbero sicuramente superare questi limiti. Se stai scrivendo la prossima Guida per autostoppisti alla galassia, non provare ad assegnare gli UUID a ciascun articolo!
Poiché la maggior parte delle risposte si è concentrata sulla teoria, penso di poter aggiungere qualcosa alla discussione dando un test pratico che ho fatto. Nel mio database ho circa 4,5 milioni di UUID generati utilizzando Java 8 UUID.randomUUID (). I seguenti sono solo alcuni che ho scoperto:
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 davvero casuale, la probabilità di avere questo tipo di UUID simili sarebbe considerevolmente bassa (vedi modifica), poiché stiamo considerando solo 4,5 milioni di voci. Quindi, sebbene questa funzione sia buona, in termini di non avere collisioni, per me non sembra così buona come sarebbe in teoria.
Modifica :
Molte persone sembrano non capire questa risposta, quindi chiarirò il mio punto: so che le somiglianze sono "piccole" e lontane da una piena collisione. Tuttavia, volevo solo confrontare UUID.randomUUID () di Java con un vero generatore di numeri casuali, che è la vera domanda.
In un vero generatore di numeri casuali, la probabilità che si verifichi l'ultimo caso dovrebbe aggirarsi intorno allo 0,007%. Pertanto, penso che la mia conclusione sia valida.
La formula è spiegata in questo articolo della wiki en.wikipedia.org/wiki/Birthday_problem
Ho giocato alla lotteria lo scorso anno e non ho mai vinto ... ma sembra che ci siano vincitori alla lotteria ...
doc: http://tools.ietf.org/html/rfc4122
Tipo 1: non implementato. la collisione è possibile se l'UUID è generato nello stesso momento. impl può essere sincronizzato artificialmente per evitare questo problema.
Tipo 2: non vedere mai un'implementazione.
Tipo 3: hash md5: possibile collisione (128 bit tecnici-2 byte)
Tipo 4: casuale: possibile collisione (come lotteria). si noti che l'impl jdk6 non usa un "vero" random random perché l'algoritmo PRNG non è scelto dallo sviluppatore e si può forzare il sistema a usare un algo PRNG "scadente". Quindi il tuo UUID è prevedibile.
Tipo 5: hash sha1: non implementato: possibile collisione (byte tecnici 160 bit-2)