Generieren der v5-UUID. Was ist Name und Namespace?

124

Ich habe die manSeite gelesen , aber ich verstehe nicht, wofür nameund wofür namespace.

Für UUIDs der Versionen 3 und 5 müssen die zusätzlichen Befehlszeilenargumente Namespace und Name angegeben werden. Der Namespace ist entweder eine UUID in Zeichenfolgendarstellung oder eine Kennung für intern vordefinierte Namespace-UUIDs (derzeit bekannt sind "ns: DNS", "ns: URL", "ns: OID" und "ns: X500"). Der Name ist eine Zeichenfolge beliebiger Länge.

Der Namespace:

Der Namespace ist entweder eine UUID in Zeichenfolgendarstellung oder eine

Bedeutet das, dass ich es (UUID v4) irgendwo in Bezug auf die generierte UUID v5 speichern muss? Warum wird dies in beiden Fällen nicht automatisch durchgeführt?

Der Name ist eine Zeichenfolge beliebiger Länge.

nameeine völlig zufällige Zeichenfolge? Was ist dann der Zweck davon? Kann es von der UUID v5 dekodiert werden?

Gajus
quelle

Antworten:

106

Name und Namespace können verwendet werden, um eine Hierarchie von (sehr wahrscheinlich) eindeutigen UUIDs zu erstellen.

Grob gesagt wird eine UUID vom Typ 3 oder Typ 5 generiert, indem eine Namespace-ID mit einem Namen zusammengefügt wird. UUIDs vom Typ 3 verwenden MD5 und UUIDs vom Typ 5 verwenden SHA1. Es sind nur 128 Bit verfügbar und 5 Bit werden verwendet, um den Typ anzugeben, sodass nicht alle Hash-Bits in die UUID gelangen. (Auch MD5 gilt als kryptografisch defekt, und SHA1 befindet sich in den letzten Zügen. Verwenden Sie dies also nicht, um Daten zu überprüfen, die "sehr sicher" sein müssen.) Das heißt, es gibt Ihnen eine Möglichkeit, eine wiederholbare / überprüfbare "Hash" -Funktion zu erstellen, die einen möglicherweise hierarchischen Namen einem wahrscheinlich eindeutigen 128-Bit-Wert zuordnet, der möglicherweise wie ein hierarchischer Hash oder MAC wirkt.

Angenommen, Sie haben einen (Schlüssel-, Wert-) Speicher, der jedoch nur einen Namespace unterstützt. Mit UUIDs vom Typ 3 oder 5 können Sie eine große Anzahl unterschiedlicher logischer Namespaces generieren. Erstellen Sie zunächst eine Root-UUID für jeden Namespace. Dies kann eine UUID vom Typ 1 (Host + Zeitstempel) oder vom Typ 4 (zufällig) sein, solange Sie sie irgendwo aufbewahren. Alternativ können Sie eine zufällige UUID für Ihren Stamm erstellen (oder die nullUUID: 00000000-0000-0000-0000-000000000000als Stamm verwenden) und dann mit " uuid -v5 $ROOTUUID $NAMESPACENAME" eine reproduzierbare UUID für jeden Namespace erstellen . Jetzt können Sie eindeutige UUIDs für Schlüssel in einem Namespace mit "uuid -v5 $NAMESPACEUUID $KEYDiese UUIDs können mit hoher Wahrscheinlichkeit zur Vermeidung von Kollisionen in einen einzelnen Schlüsselwertspeicher geworfen werden. Dieser Vorgang kann rekursiv wiederholt werden, so dass beispielsweise der mit einem UUID-Schlüssel verknüpfte "Wert" wiederum eine Art logischen "Namespace darstellt "Wie ein Bucket, Container oder Verzeichnis kann seine UUID wiederum verwendet werden, um hierarchischere UUIDs zu generieren.

Die generierte UUID vom Typ 3 oder Typ 5 enthält einen (Teil-) Hash der Namespace-ID und des Namens innerhalb des Namespace (Schlüssel). Es enthält nicht mehr die Namespace-UUID als ein Nachrichten-MAC den Inhalt der Nachricht, aus der es codiert ist. Der Name ist aus Sicht des UUID-Algorithmus eine "beliebige" (Oktett-) Zeichenfolge. Die Bedeutung hängt jedoch von Ihrer Anwendung ab. Dies kann ein Dateiname in einem logischen Verzeichnis, eine Objekt-ID in einem Objektspeicher usw. sein.

Während dies für eine mäßig große Anzahl von Namespaces und Schlüsseln gut funktioniert, geht ihm schließlich der Dampf aus, wenn Sie eine sehr große Anzahl von Schlüsseln anstreben, die mit sehr hoher Wahrscheinlichkeit eindeutig sind. Der Wikipedia-Eintrag für das Geburtstagsproblem (auch bekannt als Geburtstagsparadoxon) enthält eine Tabelle, in der die Wahrscheinlichkeiten mindestens einer Kollision für verschiedene Anzahlen von Schlüsseln und Tabellengrößen angegeben sind. Für 128-Bit hat das Hashing von 26 Milliarden Schlüsseln auf diese Weise eine Kollisionswahrscheinlichkeit von p=10^-18(vernachlässigbar), aber 26 Billionen Schlüssel, erhöht die Wahrscheinlichkeit von mindestens einer Kollision auf p=10^-12(eins zu einer Billion), und das Hashing von 26*10^15Schlüsseln erhöht die Wahrscheinlichkeit von mindestens eine Kollision mitp=10^-6(einer von einer Million). Wenn Sie 5 Bits anpassen, die den UUID-Typ codieren, läuft er etwas schneller ab, sodass eine Billion Schlüssel eine Wahrscheinlichkeit von 1 zu 1 Billion für eine einzelne Kollision haben.

Die Wahrscheinlichkeitstabelle finden Sie unter http://en.wikipedia.org/wiki/Birthday_problem#Probability_table .

Weitere Informationen zu UUID-Codierungen finden Sie unter http://www.ietf.org/rfc/rfc4122.txt .

Jeff Anderson-Lee
quelle
2
Kann ich auf einer bestimmten Hierarchieebene eine UUIDv5 als Namespace und eine UUIDv4 als Zufallsschlüssel verwenden, um sicherzustellen, dass die Kollisionen in den Daten selbst (die durch diese GUID identifiziert werden) die Wahrscheinlichkeit einer Kollision von UUIDs nicht erhöhen? Gibt es Leistungsprobleme, über die ich Bescheid wissen sollte?
ermik
Ich bin neu in dem Konzept und war verwirrt darüber, um welche Hierarchie es sich handelt. Wo kann ich es sehen usw. Eine gewisse Klarheit kam, als ich mich an die Erklärung hielt. Dies könnte verwendet werden , um eine reproduzierbare UUID für den Namespace zu erstellen . Ich frage mich, ob es eine Möglichkeit gibt, zu überprüfen, ob eine bestimmte UUID (vom Typ 3 oder 5) unter Verwendung eines bestimmten Namespace (seiner UUID) generiert wurde.
msciwoj
213

UUIDs vom Typ 3 und Typ 5 sind nur eine Technik zum Einfügen eines Hashs in eine UUID.

  • Typ 1: Fügt MAC-Adresse + Datum / Uhrzeit in 128 Bit ein
  • Typ 3 : Füllt einen MD5-Hash in 128 Bit
  • Typ 4: Füllt zufällige Daten in 128 Bit
  • Typ 5 : Füllt einen SHA1-Hash in 128 Bit
  • Typ 6: Inoffizielle Idee für sequentielle UUIDs

Ein SHA1-Hash gibt 160 Bit (20 Bytes) aus; Das Ergebnis des Hashs wird in eine UUID konvertiert.

Mit dem 20-Byte-Hash von 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

(Beachten Sie, dass die ersten beiden Bits von '9' bereits 1 bzw. 0 sind, sodass dies keine Auswirkung hat.)

Was habe ich Hash?

Sie fragen sich wahrscheinlich, was ich hacken soll. Grundsätzlich haben Sie die Verkettung von:

sha1([NamespaceUUID]+[AnyString]);

Sie stellen Ihrer Zeichenfolge einen sogenannten Namespace voran , um Namenskonflikte zu vermeiden.

Der UUID-RFC definiert vier Namespaces für Sie vor:

  • 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}

Sie könnten also zusammen hashen:

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

Der RFC definiert dann, wie:

  • Nehmen Sie die 160 Bits von SHA1
  • und konvertieren Sie es in 128 Bit einer UUID

Der grundlegende Kern ist es , nur die ersten 128 Bits zu nehmen, material a 5in der Art aufnehmen, und anschließend die ersten beiden Bits des eingestellten clock_seq_hi_and_reservedAbschnitts auf 1 bzw. 0 ist.

Mehr Beispiele

Nachdem Sie nun eine Funktion haben, die einen sogenannten Namen generiert , können Sie die Funktion (im Pseudocode) haben:

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;
}

(Beachten Sie, dass die Endianität Ihres Systems die Indizes der oben genannten Bytes beeinflussen kann.)

Sie können Anrufe haben:

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');

Nun zurück zu Ihrer Frage

Für UUIDs der Versionen 3 und 5 müssen die zusätzlichen Befehlszeilenargumente Namespace und Name angegeben werden. Der Namespace ist entweder eine UUID in Zeichenfolgendarstellung oder eine Kennung für intern vordefinierte Namespace-UUIDs (derzeit bekannt sind "ns: DNS", "ns: URL", "ns: OID" und "ns: X500"). Der Name ist eine Zeichenfolge beliebiger Länge.

Der Namespace ist eine beliebige UUID. Es kann eines der vordefinierten sein, oder Sie können Ihr eigenes erstellen, z.

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

Der Name ist eine Zeichenfolge beliebiger Länge.

Der Name ist nur der Text, den Sie an den Namespace anhängen, dann hashen und in eine UUID einfügen möchten:

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');

Hinweis : Jeder öffentlich zugängliche Code. Keine Zuordnung erforderlich.

Ian Boyd
quelle
45
Danke für die gründliche Erklärung. Wenn ich Bonuspunkte dafür geben könnte, Namespace_RectalForeignExtractedObjectwürde ich.
Boodle
Ist es möglich, den Namen oder den aus der UUID dekodierten Namespace zu dekodieren?
Sathesh
3
@Sathesh Nein, es ist nicht möglich, einen Hash zu dekodieren. Hashes sind Einwegfunktionen. Beispielsweise umfasst die gesamte Star Trek TNG Blu-Ray-Sammlung 81 GB und einen Hash von C5740BBBF2429115276D4AB60A020ED3ADE01192 . Es gibt keine Möglichkeit, diesen 20-Byte-Hash wieder in 81 GB zu dekodieren. Wenn Sie es wirklich brauchen, können Sie versuchen, alle möglichen GUIDs und möglichen Zeichenfolgen zu hashen, bis Sie die Kombination finden, die das gleiche Ergebnis liefert. Mit vielem finden Sie es irgendwo zwischen für immer und ewig.
Ian Boyd
21

Ein Name ist nichts anderes als ein Bezeichner, der in einem Namespace eindeutig ist. Das Problem ist, dass Namespaces oft recht klein sind und die Namen in einem oft mit den Namen in anderen kollidieren. Zum Beispiel ist das Autokennzeichen (Name) meines Autos im DMV-Namespace meines Bundesstaates eindeutig, aber wahrscheinlich nicht weltweit eindeutig. DMVs anderer Bundesstaaten haben möglicherweise denselben Namen in ihren eigenen Namespaces verwendet. Heck, jemand anderes kann eine Telefonnummer (Name) haben, die auch übereinstimmt, weil das noch ein anderer Namespace ist, etc.

UUIDs können als Bewohner eines einzelnen Namespace angesehen werden, der so groß ist, dass er für alles einen eindeutigen Namen liefern kann . das ist was "universell" bedeutet. Aber wie ordnet man vorhandene Namen in anderen Namespaces einer UUID zu?

Eine naheliegende Lösung besteht darin, für jedes Element eine UUID (V1 oder V4) zu generieren, um die alten Namen in ihren disjunkten Namespaces zu ersetzen. Der Nachteil ist, dass sie viel größer sind. Sie müssen alle neuen Namen an alle weitergeben, die eine Kopie Ihres Datensatzes haben, alle Ihre APIs aktualisieren usw. Wahrscheinlich können Sie die alten Namen nicht vollständig entfernen Wie auch immer, was bedeutet, dass jetzt jeder Gegenstand zwei Namen hat. Hast du es also besser oder schlechter gemacht?

Hier kommt V3 / V5 ins Spiel. Die UUIDs sehen genauso zufällig aus wie V4, sind aber tatsächlich deterministisch. Jeder, der die richtige UUID für einen Namespace hat, kann dann unabhängig dieselbe UUID für einen bestimmten Namen in diesem Namespace generieren. Sie müssen sie überhaupt nicht veröffentlichen oder vorab generieren, da jeder sie bei Bedarf im laufenden Betrieb erstellen kann!

DNS-Namen und URLs sind sehr häufig verwendete Namespaces, daher wurden für diese Standard-UUIDs veröffentlicht. ASN.1-OIDs und X.500-Namen sind nicht so häufig, aber Standard-Gremien lieben sie. Deshalb haben sie auch Standard-Namespace-UUIDs für sie veröffentlicht.

Für alle anderen Namespaces müssen Sie Ihre eigene Namespace-UUID (V1 oder V4) generieren und an alle weitergeben, die sie benötigen. Wenn Sie mehrere Namespaces haben, ist es eindeutig nicht ideal, die UUID für jeden zu veröffentlichen.

Hier kommt die Hierarchie ins Spiel: Sie erstellen eine "Basis" -UUID (von welchem ​​Typ auch immer) und verwenden diese dann als Namespace für die Benennung Ihrer anderen Namespaces! Auf diese Weise müssen Sie nur die Basis-UUID veröffentlichen (oder eine offensichtliche verwenden), und jeder kann den Rest berechnen.

Bleiben wir zum Beispiel, wir wollten einige UUIDs für StackOverflow erstellen. Das hat einen offensichtlichen Namen innerhalb des DNS-Namespace, daher ist die Basis offensichtlich:

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

StackOverflow selbst verfügt über separate Namespaces für Benutzer, Fragen, Antworten, Kommentare usw., aber diese sind auch ziemlich offensichtlich:

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');

Diese spezielle Frage lautet # 10867405, daher lautet die UUID:

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

Beachten Sie, dass dieser Prozess nichts Zufälliges enthält, sodass jeder, der der gleichen Logik folgt, die gleiche Antwort erhält. Der UUID-Namespace ist jedoch so umfangreich, dass er (angesichts der Sicherheit eines kryptografischen 122-Bit-Hashs) niemals mit einem kollidiert UUID, die aus einem anderen Namespace / Name-Paar generiert wurde.

StephenS
quelle
Ich frage mich, warum der Stackoverflow einer UUID eine eindeutig generierte große Ganzzahl zuordnen muss, da ihre APIs anscheinend nur die große Ganzzahl als Zeichenfolge zurückgeben. Wo würde die UUID verwendet, wenn nicht in der API. Es scheint, wir sollten entweder eine UUID oder BIGINT auswählen? Warum diese hybride Strategie? Noch +1 für die klare Erklärung in Ihrer Antwort.
Nishant
4
UUID V3 / V5 wurde entwickelt, wenn Sie vorhandene (und wahrscheinlich kollidierende) Namespaces deterministisch in einen UUID-Namespace konvertieren müssen. Dies ist häufig beim Zusammenführen von Datasets hilfreich. Wenn dies nicht für Ihre Arbeit gilt, wählen Sie V1 / V4.
StephenS