Generazione dell'UUID v5. Cosa sono il nome e lo spazio dei nomi?


125

Ho letto la manpagina, ma io non capirne cosa namee namespacesono per.

Per gli UUID versione 3 e 5, è necessario fornire lo spazio dei nomi e il nome degli argomenti della riga di comando aggiuntivi. Lo spazio dei nomi è un UUID nella rappresentazione di stringa o un identificatore per gli UUID dello spazio dei nomi predefiniti internamente (attualmente noti sono "ns: DNS", "ns: URL", "ns: OID" e "ns: X500"). Il nome è una stringa di lunghezza arbitraria.

Lo spazio dei nomi:

Lo spazio dei nomi è un UUID nella rappresentazione di stringa o un file

Significa che devo memorizzarlo (UUID v4) da qualche parte in relazione all'UUID generato v5? In entrambi i casi, perché ciò non viene eseguito automaticamente?

Il nome è una stringa di lunghezza arbitraria.

nameuna stringa completamente casuale? Qual è lo scopo allora? Può essere decodificato dall'UUID v5?

Risposte:


106

Il nome e lo spazio dei nomi possono essere utilizzati per creare una gerarchia di (molto probabilmente) UUID univoci.

In parole povere, un UUID di tipo 3 o 5 viene generato combinando un identificatore dello spazio dei nomi con un nome. Gli UUID di tipo 3 usano MD5 e gli UUID di tipo 5 usano SHA1. Sono disponibili solo 128 bit e vengono utilizzati 5 bit per specificare il tipo, quindi tutti i bit hash non vengono inseriti nell'UUID. (Anche MD5 è considerato crittograficamente danneggiato e SHA1 è al limite, quindi non usarlo per verificare i dati che devono essere "molto sicuri"). Detto questo, ti dà un modo per creare una funzione "hash" ripetibile / verificabile mappando un nome possibilmente gerarchico su un valore a 128 bit probabilisticamente univoco, agendo potenzialmente come un hash gerarchico o MAC.

Supponi di avere un archivio (chiave, valore), ma supporta solo uno spazio dei nomi. È possibile generare un numero elevato di spazi dei nomi logici distinti utilizzando UUID di tipo 3 o 5. Innanzitutto, crea un UUID radice per ogni spazio dei nomi. Questo potrebbe essere un UUID di tipo 1 (host + timestamp) o di tipo 4 (casuale) a condizione che lo riponi da qualche parte. In alternativa puoi creare un UUID casuale per la tua root (o usare l' nullUUID: 00000000-0000-0000-0000-000000000000come root) e quindi creare un UUID riproducibile per ogni spazio dei nomi usando " uuid -v5 $ROOTUUID $NAMESPACENAME". Ora puoi creare UUID univoci per le chiavi all'interno di uno spazio dei nomi utilizzando "uuid -v5 $NAMESPACEUUID $KEY". Questi UUID possono essere inseriti in un unico archivio di valori-chiave con alta probabilità di evitare collisioni. Questo processo può essere ripetuto in modo ricorsivo in modo che se, ad esempio, il" valore "associato a una chiave UUID rappresenta a sua volta una sorta di" spazio dei nomi logico " "come un bucket, un contenitore o una directory, il suo UUID può essere utilizzato a sua volta per generare UUID più gerarchici.

L'UUID di tipo 3 o 5 generato contiene un hash (parziale) dell'id dello spazio dei nomi e dello spazio dei nomi (chiave). Non contiene più l'UUID dello spazio dei nomi di quanto un messaggio MAC contiene il contenuto del messaggio da cui è codificato. Il nome è una stringa "arbitraria" (ottetto) dal punto di vista dell'algoritmo uuid. Il suo significato, tuttavia, dipende dalla tua applicazione. Potrebbe essere un nome di file all'interno di una directory logica, object-id all'interno di un object-store, eccetera.

Anche se funziona bene per un numero moderatamente elevato di spazi dei nomi e chiavi, alla fine si esaurisce se si punta a un numero molto elevato di chiavi uniche con una probabilità molto alta. La voce di Wikipedia per il problema del compleanno (noto anche come paradosso del compleanno) include una tabella che fornisce le probabilità di almeno una collisione per vari numeri di chiavi e dimensioni di tabella. Per 128 bit, l'hashing di 26 miliardi di chiavi in ​​questo modo ha una probabilità di collisione di p=10^-18(trascurabile), ma 26 trilioni di chiavi, aumenta la probabilità di almeno una collisione su p=10^-12(uno su un trilione) e l'hashing delle 26*10^15chiavi aumenta la probabilità di almeno una collisione ap=10^-6 (uno su un milione) . Regolando per 5 bit che codificano il tipo di UUID, si esaurirà un po 'più velocemente, quindi un trilione di chiavi ha all'incirca una probabilità di 1 su un trilione di avere una singola collisione.

Vedi http://en.wikipedia.org/wiki/Birthday_problem#Probability_table per la tabella delle probabilità.

Vedi http://www.ietf.org/rfc/rfc4122.txt per maggiori dettagli sulle codifiche UUID.


2
Ad un certo livello più in basso nella gerarchia, posso utilizzare un UUIDv5 come spazio dei nomi e UUIDv4 come chiave casuale per garantire che le collisioni nei dati stessi (che vengono identificati da questo GUID) non aumentino le possibilità di collisione degli UUID? Qualche problema di prestazioni di cui dovrei essere a conoscenza?
ermik

Sono nuovo al concetto ed ero perplesso di quale fosse la gerarchia di cui stai parlando. Dove posso vederlo, ecc ... Un po 'di chiarezza è arrivata una volta che mi sono bloccato sulla spiegazione che potrebbe essere usata per creare un UUID riproducibile per lo spazio dei nomi . Mi chiedo, esiste un modo per verificare che un determinato UUID (di tipo 3 o 5) sia stato generato utilizzando un particolare spazio dei nomi (il suo UUID)?
msciwoj

213

Gli UUID di tipo 3 e 5 sono solo una tecnica per inserire un hash in un UUID.

  • Tipo 1: inserisce l'indirizzo MAC + datetime in 128 bit
  • Tipo 3 : inserisce un hash MD5 in 128 bit
  • Tipo 4: inserisce dati casuali in 128 bit
  • Tipo 5 : inserisce un hash SHA1 in 128 bit
  • Tipo 6: idea non ufficiale per UUID sequenziali

Un hash SHA1 emette 160 bit (20 byte); il risultato dell'hash viene convertito in un UUID.

Con l'hash a 20 byte da SHA1:

SHA1 Digest:   74738ff5 5367 e958 9aee 98fffdcd1876 94028007
UUID (v5):     74738ff5-5367-5958-9aee-98fffdcd1876
                             ^_low nibble is set to 5, to indicate type 5
                                  ^_first two bits set to 1 and 0, respectively

(Notare che i primi due bit di "9" sono già rispettivamente 1 e 0, quindi questo non ha effetto).

Cosa devo hashish?

Probabilmente ti starai chiedendo cosa dovrei hashish. Fondamentalmente hai hash la concatenazione di:

sha1([NamespaceUUID]+[AnyString]);

Aggiungi un prefisso alla stringa con un cosiddetto spazio dei nomi per evitare conflitti di nome.

L' UUID RFC pre-definisce quattro spazi dei nomi per te:

  • NameSpace_DNS: {6ba7b810-9dad-11d1-80b4-00c04fd430c8}
  • NameSpace_URL: {6ba7b811-9dad-11d1-80b4-00c04fd430c8}
  • NameSpace_OID: {6ba7b812-9dad-11d1-80b4-00c04fd430c8}
  • NameSpace_X500: {6ba7b814-9dad-11d1-80b4-00c04fd430c8}

Quindi, potresti hash insieme:

StackOverflowDnsUUID = sha1(Namespace_DNS + "stackoverflow.com");
StackOverflowUrlUUID = sha1(Namespace_URL + "stackoverflow.com");

L'RFC definisce quindi come:

  • prendere i 160 bit da SHA1
  • e convertirlo in 128 bit di un UUID

L'essenza di base è prendere solo i primi 128 bit, inserire a 5nel record del tipo e quindi impostare i primi due bit della clock_seq_hi_and_reservedsezione rispettivamente su 1 e 0.

Altri esempi

Ora che hai una funzione che genera un cosiddetto Nome , puoi avere la funzione (in pseudo-codice):

UUID NameToUUID(UUID NamespaceUUID, String Name)
{
    byte[] hash = sha1(NamespaceUUID.ToBytes() + Name.ToBytes());
    UUID result;
    Copy(hash, result, 16);
    result[6] &= 0x0F; 
    result[6] |= 0x50;
    result[8] &= 0x3F; 
    result[8] |= 0x80;
    return result;
}

(Nota che l'endianità del tuo sistema può influenzare gli indici dei byte sopra)

Puoi avere chiamate:

uuid = NameToUUID(Namespace_DNS, 'www.stackoverflow.com');
uuid = NameToUUID(Namespace_DNS, 'www.google.com');
uuid = NameToUUID(Namespace_URL, 'http://www.stackoverflow.com');
uuid = NameToUUID(Namespace_URL, 'http://www.google.com/search&q=rfc+4112');
uuid = NameToUUID(Namespace_URL, 'http://stackoverflow.com/questions/5515880/test-vectors-for-uuid-version-5-converting-hash-into-guid-algorithm');

Ora torniamo alla tua domanda

Per gli UUID versione 3 e 5, è necessario fornire lo spazio dei nomi e il nome degli argomenti della riga di comando aggiuntivi. Lo spazio dei nomi è un UUID nella rappresentazione di stringa o un identificatore per gli UUID dello spazio dei nomi predefiniti internamente (attualmente noti sono "ns: DNS", "ns: URL", "ns: OID" e "ns: X500"). Il nome è una stringa di lunghezza arbitraria.

Lo spazio dei nomi è qualsiasi UUID che ti piace. Può essere uno di quelli predefiniti, oppure puoi crearne uno tuo, ad esempio:

UUID Namespace_RectalForeignExtractedObject = '8e884ace-bee4-11e4-8dfc-aa07a5b093db'

Il nome è una stringa di lunghezza arbitraria.

Il nome è solo il testo che desideri venga aggiunto allo spazio dei nomi, quindi sottoposto a hashing e inserito in un UUID:

uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'screwdriver');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'toothbrush');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'broomstick');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'orange');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'axe handle');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'impulse body spray');
uuid = NameToUUID('8e884ace-bee4-11e4-8dfc-aa07a5b093db', 'iPod Touch');

Nota : qualsiasi codice rilasciato nel dominio pubblico. Nessuna attribuzione richiesta.


45
Grazie per la spiegazione approfondita. Se potessi dare punti bonus, Namespace_RectalForeignExtractedObjectlo farei.
boodle

È possibile decodificare il nome o lo spazio dei nomi decodificato dall'UUID?
Sathesh

4
@Sathesh No, non è possibile decodificare un hash; gli hash sono funzioni unidirezionali. Ad esempio, l'intera collezione di Blu-Ray di Star Trek TNG è di 81 GB e ha un hash di C5740BBBF2429115276D4AB60A020ED3ADE01192 . Non c'è modo di decodificare quell'hash di 20 byte in 81 GB. Se ne hai davvero bisogno, puoi provare a eseguire l'hashing di tutti i possibili GUID e le possibili stringhe finché non trovi la combinazione che dà lo stesso risultato. Con qualsiasi pranzo lo troverai da qualche parte tra l'eternità e l'eternità.
Ian Boyd

22

Un nome non è altro che un identificatore univoco all'interno di uno spazio dei nomi. Il problema è che gli spazi dei nomi sono spesso piuttosto piccoli e i nomi in uno spesso entrano in conflitto con i nomi in altri. Ad esempio, il numero di targa della mia auto (nome) è unico nello spazio dei nomi del DMV del mio stato, ma probabilmente non è unico al mondo; DMV di altri stati potrebbero aver utilizzato lo stesso nome nei propri spazi dei nomi. Diamine, qualcun altro potrebbe avere un numero di telefono (nome) che corrisponde anche perché è ancora un altro spazio dei nomi, ecc.

Gli UUID possono essere visti come se abitassero un singolo spazio dei nomi così vasto da poter fornire un nome univoco per tutto ; questo è ciò che significa "universale". Ma come mappare i nomi esistenti in altri spazi dei nomi a un UUID?

Una soluzione ovvia è generare un UUID (V1 o V4) per ogni elemento per sostituire i vecchi nomi nei loro spazi dei nomi disgiunti. Lo svantaggio è che sono molto più grandi, devi comunicare tutti i nuovi nomi a tutti coloro che hanno una copia del tuo set di dati, aggiornare tutte le tue API, ecc. È probabile che tu non possa effettivamente sbarazzarti completamente dei vecchi nomi comunque, il che significa che ora ogni oggetto ha due nomi, quindi hai migliorato o peggiorato le cose?

È qui che entrano in gioco V3 / V5. Gli UUID sembrano casuali come V4 ma in realtà sono deterministici; chiunque abbia l'UUID corretto per uno spazio dei nomi può quindi generare in modo indipendente lo stesso UUID per qualsiasi nome all'interno di quello spazio dei nomi. Non è necessario pubblicarli affatto né pre-generarli poiché chiunque può crearli al volo secondo necessità!

I nomi DNS e gli URL sono spazi dei nomi usati molto comunemente, quindi per questi sono stati pubblicati gli UUID standard; Gli OID ASN.1 ei nomi X.500 non sono così comuni, ma gli organismi di standard li adorano, quindi hanno pubblicato anche gli UUID dello spazio dei nomi standard per loro.

Per tutti gli altri spazi dei nomi, è necessario generare il proprio UUID dello spazio dei nomi (V1 o V4) e comunicarlo a chiunque ne abbia bisogno. Se hai più spazi dei nomi, dover pubblicare l'UUID per ciascuno non è chiaramente l'ideale.

È qui che entra in gioco la gerarchia: crei un UUID "di base" (di qualsiasi tipo), e poi lo usi come spazio dei nomi per nominare gli altri spazi dei nomi! In questo modo, devi solo pubblicare l'UUID di base (o usarne uno ovvio) e tutti possono calcolare il resto.

Ad esempio, rimaniamo che volevamo creare alcuni UUID per StackOverflow; che ha un nome ovvio all'interno dello spazio dei nomi DNS, quindi la base è ovvia:

uuid ns_dns = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
uuid ns_base = uuidv5(ns_dns, 'stackoverflow.com');

StackOverflow stesso ha spazi dei nomi separati per utenti, domande, risposte, commenti, ecc., Ma anche quelli sono abbastanza ovvi:

uuid ns_user = uuidv5(ns_base, 'user');
uuid ns_question = uuidv5(ns_base, 'question');
uuid ns_answer = uuidv5(ns_base, 'answer');
uuid ns_comment = uuidv5(ns_base, 'comment');

Questa particolare domanda è # 10867405, quindi il suo UUID sarebbe:

uuid here = uuidv5(ns_question, '10867405');

Si noti che non c'è nulla di casuale in questo processo, quindi chiunque segua la stessa logica otterrà la stessa risposta, ma lo spazio dei nomi UUID è così vasto che (effettivamente, data la sicurezza di un hash crittografico a 122 bit) non entrerà mai in collisione con un UUID generato da qualsiasi altra coppia spazio dei nomi / nome.


Mi chiedo perché stackoverflow debba mappare un intero grande generato in modo univoco a un UUID dato che le sue API apparentemente restituiscono solo il numero intero grande come stringa. Dove dovrebbe essere utilizzato l'UUID se non nell'API. Sembra che dovremmo selezionare un UUID o BIGINT? Perché questa strategia ibrida. Ancora +1 per la chiara spiegazione nella tua risposta.
nishant

4
Gli UUID V3 / V5 sono stati progettati per quando è necessario convertire in modo deterministico gli spazi dei nomi esistenti (e probabilmente in collisione) in uno spazio dei nomi UUID, che è spesso utile quando si uniscono set di dati. Se questo non si applica a quello che stai facendo, allora vai con V1 / V4.
StephenS
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.