กำลังสร้าง v5 UUID ชื่อและเนมสเปซคืออะไร?


125

ฉันได้อ่านmanหน้า แต่ฉันทำไม่ได้ undestand สิ่งที่nameและnamespaceมีการ

สำหรับ UUID เวอร์ชัน 3 และเวอร์ชัน 5 จะต้องกำหนดเนมสเปซและชื่ออาร์กิวเมนต์บรรทัดคำสั่งเพิ่มเติม เนมสเปซเป็น UUID ในการแสดงสตริงหรือตัวระบุสำหรับ UUID ของเนมสเปซที่กำหนดไว้ล่วงหน้าภายใน (ปัจจุบันรู้จักคือ "ns: DNS", "ns: URL", "ns: OID" และ "ns: X500") ชื่อนี้เป็นสตริงที่มีความยาวโดยพลการ

เนมสเปซ:

เนมสเปซเป็น UUID ในการแสดงสตริงหรือ

หมายความว่าฉันต้องจัดเก็บ (UUID v4) ไว้ที่ไหนสักแห่งที่สัมพันธ์กับ UUID v5 ที่สร้างขึ้น? ไม่ว่าในกรณีใดเหตุใดจึงไม่ดำเนินการโดยอัตโนมัติ

ชื่อนี้เป็นสตริงที่มีความยาวโดยพลการ

nameสตริงสุ่มอย่างสมบูรณ์? จุดประสงค์ของมันคืออะไร? สามารถถอดรหัสจาก UUID v5 ได้หรือไม่?

คำตอบ:


107

ชื่อและเนมสเปซสามารถใช้เพื่อสร้างลำดับชั้นของ UUID ที่ไม่ซ้ำกัน (อาจเป็นไปได้มาก)

กล่าวโดยคร่าวๆ UUID ประเภท 3 หรือประเภท 5 ถูกสร้างขึ้นโดยการรวมตัวระบุเนมสเปซด้วยชื่อเข้าด้วยกัน UUID ประเภท 3 ใช้ MD5 และ UUID ประเภท 5 ใช้ SHA1 มีเพียง 128 บิตและใช้ 5 บิตในการระบุประเภทดังนั้นบิตแฮชทั้งหมดจะไม่ทำให้เป็น UUID (นอกจากนี้ MD5 ยังถูกพิจารณาว่าเสียการเข้ารหัสและ SHA1 อยู่ในขาสุดท้ายดังนั้นอย่าใช้สิ่งนี้เพื่อยืนยันข้อมูลที่ต้อง "ปลอดภัยมาก") ที่กล่าวว่ามันช่วยให้คุณสามารถสร้างฟังก์ชัน "แฮช" ที่ทำซ้ำได้ / ตรวจสอบได้โดยแมปชื่อตามลำดับชั้นที่อาจเป็นไปได้ลงบนค่า 128 บิตที่ไม่ซ้ำกันซึ่งอาจทำหน้าที่เหมือนแฮชแบบลำดับชั้นหรือ MAC

สมมติว่าคุณมีที่เก็บ (คีย์ค่า) แต่รองรับเนมสเปซเดียวเท่านั้น คุณสามารถสร้างเนมสเปซแบบโลจิคัลที่แตกต่างกันจำนวนมากโดยใช้ UUID ประเภท 3 หรือประเภท 5 ขั้นแรกให้สร้าง UUID รูทสำหรับแต่ละเนมสเปซ นี่อาจเป็น UUID ประเภท 1 (โฮสต์ + การประทับเวลา) หรือประเภท 4 (สุ่ม) ตราบใดที่คุณเก็บไว้ที่ไหนสักแห่ง หรือคุณสามารถสร้างหนึ่ง UUID สุ่มสำหรับรากของคุณ (หรือใช้nullUUID: 00000000-0000-0000-0000-000000000000เป็น root) และสร้าง UUID ทำซ้ำสำหรับแต่ละ namespace ใช้ " uuid -v5 $ROOTUUID $NAMESPACENAME" ตอนนี้คุณสามารถสร้าง UUID เฉพาะสำหรับคีย์ภายในเนมสเปซโดยใช้ "uuid -v5 $NAMESPACEUUID $KEY". UUID เหล่านี้สามารถโยนลงในที่เก็บคีย์ - ค่าเดียวซึ่งมีความเป็นไปได้สูงที่จะหลีกเลี่ยงการชนกันกระบวนการนี้สามารถทำซ้ำแบบวนซ้ำเพื่อให้ตัวอย่างเช่น" ค่า "ที่เชื่อมโยงกับคีย์ UUID จะแสดงถึงเนมสเปซเชิงตรรกะ" บางประเภท "เช่นที่เก็บข้อมูลคอนเทนเนอร์หรือไดเร็กทอรีจากนั้นสามารถใช้ UUID เพื่อสร้าง UUID ตามลำดับชั้นได้มากขึ้น

UUID ประเภท 3 หรือประเภท 5 ที่สร้างขึ้นมีแฮช (บางส่วน) ของรหัสเนมสเปซและ name-within-namespace (คีย์) ไม่มีการเก็บ UUID ของเนมสเปซมากไปกว่าข้อความที่ MAC เก็บเนื้อหาของข้อความที่เข้ารหัสไว้ ชื่อนี้เป็นสตริง "โดยพลการ" (อ็อกเต็ต) จากมุมมองของอัลกอริทึม uuid ความหมายขึ้นอยู่กับใบสมัครของคุณ อาจเป็นชื่อไฟล์ภายในโลจิคัลไดเร็กทอรีอ็อบเจ็กต์ id ภายในที่เก็บอ็อบเจ็กต์ ฯลฯ

แม้ว่าจะใช้งานได้ดีกับเนมสเปซและคีย์จำนวนมากในระดับปานกลาง แต่ในที่สุดก็จะหมดลงหากคุณต้องการคีย์จำนวนมากที่ไม่ซ้ำกันซึ่งมีโอกาสสูงมาก รายการ Wikipedia สำหรับปัญหาวันเกิด (aka Birthday Paradox) ประกอบด้วยตารางที่ให้ความน่าจะเป็นของการชนกันอย่างน้อยหนึ่งครั้งสำหรับจำนวนคีย์และขนาดตารางต่างๆ สำหรับ 128 บิตการแฮชคีย์ 26 พันล้านคีย์ด้วยวิธีนี้มีความเป็นไปได้ที่จะเกิดการชนกันp=10^-18(เล็กน้อย) แต่ 26 ล้านล้านคีย์จะเพิ่มความน่าจะเป็นของการชนกันอย่างน้อยหนึ่งครั้งเป็นp=10^-12(หนึ่งในล้านล้าน) และการแฮช26*10^15คีย์เพิ่มความน่าจะเป็นของ อย่างน้อยหนึ่งการชนกับp=10^-6(หนึ่งในล้าน). การปรับสำหรับ 5 บิตที่เข้ารหัสประเภท UUID จะทำงานได้เร็วขึ้นเล็กน้อยดังนั้นคีย์ล้านล้านคีย์จึงมีโอกาสประมาณ 1 ในล้านล้านที่จะเกิดการชนกันเพียงครั้งเดียว

ดูhttp://en.wikipedia.org/wiki/Birthday_problem#Probability_tableสำหรับตารางความน่าจะเป็น

ดูhttp://www.ietf.org/rfc/rfc4122.txtสำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการเข้ารหัส UUID


2
ในระดับหนึ่งตามลำดับชั้นฉันสามารถใช้ UUIDv5 เป็นเนมสเปซและ UUIDv4 เป็นคีย์แบบสุ่มเพื่อให้แน่ใจว่าข้อมูลจะชนกัน (ที่ GUID ระบุ) ไม่เพิ่มโอกาสในการชนกันของ UUID หรือไม่ ปัญหาด้านประสิทธิภาพใด ๆ ที่ฉันควรทราบ
ermik

ฉันยังใหม่กับแนวคิดนี้และรู้สึกงงงวยว่าลำดับชั้นที่คุณกำลังพูดถึงคืออะไร ฉันสามารถเห็นมัน ฯลฯ ... ความชัดเจนบางคนมาเมื่อฉันติดอยู่ในคำอธิบายนี้อาจถูกนำมาใช้เพื่อสร้าง UUID ทำซ้ำสำหรับ namespace ฉันสงสัยว่ามีวิธีตรวจสอบว่า UUID ที่ระบุ (ประเภท 3 หรือ 5) ถูกสร้างขึ้นโดยใช้เนมสเปซเฉพาะ (UUID) หรือไม่
msciwoj

214

UUID ประเภทที่ 3 และประเภทที่ 5 เป็นเพียงเทคนิคในการบรรจุแฮชลงใน UUID

แฮช SHA1 เอาต์พุต 160 บิต (20 ไบต์); ผลลัพธ์ของแฮชจะถูกแปลงเป็น UUID

ด้วยแฮช 20 ไบต์จาก 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

(โปรดทราบว่าสองบิตแรกของ '9' มีค่า 1 และ 0 อยู่แล้วตามลำดับดังนั้นจึงไม่มีผล)

ฉันแฮชอะไร

คุณคงสงสัยว่าฉันควรจะแฮชอะไร โดยทั่วไปคุณแฮชการเชื่อมต่อของ:

sha1([NamespaceUUID]+[AnyString]);

คุณนำหน้าสตริงของคุณด้วยเนมสเปซที่เรียกว่าเพื่อป้องกันความขัดแย้งของชื่อ

UUID RFCก่อนกำหนดสี่ namespaces สำหรับคุณ:

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

ดังนั้นคุณสามารถแฮชด้วยกัน:

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

จากนั้น RFC จะกำหนดวิธีการ:

  • รับ 160 บิตจาก SHA1
  • และแปลงเป็น UUID 128 บิต

สาระสำคัญพื้นฐานคือรับเฉพาะ 128 บิตแรกยัด a 5ในเร็กคอร์ดtypeจากนั้นตั้งค่าสองบิตแรกของclock_seq_hi_and_reservedส่วนเป็น 1 และ 0 ตามลำดับ

ตัวอย่างเพิ่มเติม

เมื่อคุณมีฟังก์ชันที่สร้างชื่อที่เรียกว่าคุณสามารถมีฟังก์ชัน (ในรหัสหลอก):

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

(โปรดทราบว่า endian-ness ของระบบของคุณอาจส่งผลต่อดัชนีของไบต์ข้างต้น)

คุณสามารถโทรได้:

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

ตอนนี้กลับไปที่คำถามของคุณ

สำหรับ UUID เวอร์ชัน 3 และเวอร์ชัน 5 จะต้องกำหนดเนมสเปซและชื่ออาร์กิวเมนต์บรรทัดคำสั่งเพิ่มเติม เนมสเปซเป็น UUID ในการแสดงสตริงหรือตัวระบุสำหรับ UUID ของเนมสเปซที่กำหนดไว้ล่วงหน้าภายใน (ปัจจุบันรู้จักคือ "ns: DNS", "ns: URL", "ns: OID" และ "ns: X500") ชื่อนี้เป็นสตริงที่มีความยาวโดยพลการ

namespaceคือสิ่งที่คุณชอบ UUID อาจเป็นหนึ่งในสิ่งที่กำหนดไว้ล่วงหน้าหรือคุณสามารถสร้างขึ้นเองเช่น:

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

ชื่อนี้เป็นสตริงที่มีความยาวโดยพลการ

ชื่อเป็นเพียงข้อความที่คุณต้องการต่อท้ายเนมสเปซจากนั้นแฮชและยัดลงใน 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');

หมายเหตุ : รหัสใด ๆ ที่เผยแพร่สู่สาธารณสมบัติ ไม่จำเป็นต้องแสดงที่มา


45
ขอบคุณสำหรับคำอธิบายอย่างละเอียด ถ้าฉันสามารถให้คะแนนโบนัสNamespace_RectalForeignExtractedObjectได้
boodle

เป็นไปได้ไหมที่จะถอดรหัสชื่อหรือเนมสเปซที่ถอดรหัสจาก UUID
Sathesh

4
@Sathesh ไม่มันเป็นไปไม่ได้ที่จะถอดรหัสแฮช; แฮชเป็นฟังก์ชันทางเดียว ยกตัวอย่างเช่นทั้งคอลเลกชัน Star Trek TNG Blu-Ray 81 GB และมีกัญชาของC5740BBBF2429115276D4AB60A020ED3ADE01192 ไม่มีวิธีถอดรหัสแฮชขนาด 20 ไบต์นั้นกลับไปเป็น 81 GB หากคุณต้องการจริงๆคุณสามารถลองแฮช GUID และสตริงที่เป็นไปได้ทั้งหมดจนกว่าคุณจะพบชุดค่าผสมที่ให้ผลลัพธ์เหมือนกัน ด้วยความโชคดีใด ๆ คุณจะพบว่ามันอยู่ระหว่างนิรันดร์และนิรันดร์
Ian Boyd

22

ชื่อไม่ได้เป็นอะไรมากไปกว่าตัวระบุที่ไม่ซ้ำกันภายในบางเนมสเปซ ปัญหาคือเนมสเปซมักมีขนาดค่อนข้างเล็กและชื่อในชื่อมักจะชนกับชื่ออื่น ๆ ตัวอย่างเช่นหมายเลขป้ายทะเบียนรถของฉัน (ชื่อ) ไม่ซ้ำกันภายในเนมสเปซ DMV ของรัฐของฉัน แต่อาจไม่ซ้ำกันในโลก DMV ของสถานะอื่นอาจใช้ชื่อเดียวกันในเนมสเปซของตนเอง เฮ้คนอื่นอาจมีหมายเลขโทรศัพท์ (ชื่อ) ที่ตรงกันเนื่องจากเป็นเนมสเปซอื่นเป็นต้น

UUIDs สามารถมองเห็นเป็นที่พำนัก namespace ที่เดียวจึงมากมายที่จะสามารถระบุชื่อไม่ซ้ำกันสำหรับทุกอย่าง ; นั่นคือความหมายของ "สากล" แต่คุณจะแมปชื่อที่มีอยู่ในเนมสเปซอื่นกับ UUID ได้อย่างไร?

วิธีแก้ปัญหาที่ชัดเจนวิธีหนึ่งคือการสร้าง UUID (V1 หรือ V4) สำหรับทุกรายการเพื่อแทนที่ชื่อเก่าในเนมสเปซที่ไม่ปะติดปะต่อกัน ข้อเสียคือมันใหญ่กว่ามากคุณต้องสื่อสารชื่อใหม่ทั้งหมดกับทุกคนที่มีสำเนาชุดข้อมูลของคุณอัปเดต API ทั้งหมดของคุณ ฯลฯ โอกาสที่คุณไม่สามารถกำจัดชื่อเก่าได้ทั้งหมด อย่างไรก็ตามซึ่งหมายความว่าตอนนี้ทุกรายการมีสองชื่อคุณทำให้สิ่งต่างๆดีขึ้นหรือแย่ลง?

นี่คือจุดที่ V3 / V5 เข้ามา UUID มีลักษณะสุ่มเหมือนกับ V4 แต่แท้จริงแล้วถูกกำหนด ทุกคนที่มีความเหมาะสมสำหรับ UUID namespace สามารถแล้วอิสระสร้าง UUID เดียวกันสำหรับชื่อใดก็ตามภายใน namespace ว่า คุณไม่จำเป็นต้องเผยแพร่เลยหรือแม้แต่สร้างไว้ล่วงหน้าเนื่องจากใคร ๆ ก็สามารถสร้างได้ทันทีตามต้องการ!

ชื่อ DNS และ URL เป็นเนมสเปซที่ใช้กันทั่วไปดังนั้น UUID มาตรฐานจึงถูกเผยแพร่สำหรับสิ่งเหล่านั้น ชื่อ ASN.1 OIDs และ X.500 นั้นไม่เหมือนกัน แต่หน่วยงานมาตรฐานก็ชอบพวกเขาดังนั้นพวกเขาจึงเผยแพร่เนมสเปซมาตรฐานสำหรับพวกเขาด้วย

สำหรับเนมสเปซอื่น ๆ ทั้งหมดคุณต้องสร้างเนมสเปซ UUID (V1 หรือ V4) ของคุณเองและสื่อสารกับทุกคนที่ต้องการ หากคุณมีหลายเนมสเปซการต้องเผยแพร่ UUID สำหรับแต่ละเนมสเปซนั้นไม่เหมาะอย่างชัดเจน

นี่คือที่มาของลำดับชั้น: คุณสร้าง UUID "ฐาน" หนึ่งตัว (ไม่ว่าจะเป็นประเภทใดก็ตาม) จากนั้นใช้เป็นเนมสเปซสำหรับตั้งชื่อเนมสเปซอื่นของคุณ! ด้วยวิธีนี้คุณจะต้องเผยแพร่ UUID พื้นฐานเท่านั้น (หรือใช้ UUID ที่ชัดเจน) และทุกคนสามารถคำนวณส่วนที่เหลือได้

ตัวอย่างเช่นเราต้องการสร้าง UUID สำหรับ StackOverflow ที่มีชื่อที่ชัดเจนภายในเนมสเปซ DNS ดังนั้นฐานจึงชัดเจน:

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

StackOverflow มีเนมสเปซแยกต่างหากสำหรับผู้ใช้คำถามคำตอบความคิดเห็น ฯลฯ แต่สิ่งเหล่านี้ก็ค่อนข้างชัดเจนเช่นกัน:

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

คำถามนี้คือ # 10867405 ดังนั้น UUID จะเป็น:

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

สังเกตว่าไม่มีอะไรสุ่มในกระบวนการนี้ดังนั้นใครก็ตามที่ทำตามตรรกะเดียวกันจะได้รับคำตอบเหมือนกัน แต่เนมสเปซ UUID นั้นมีขนาดใหญ่มากจน (ได้รับการรักษาความปลอดภัยของแฮชการเข้ารหัส 122 บิต) จะไม่ชนกับ UUID ที่สร้างจากคู่เนมสเปซ / ชื่ออื่น ๆ


ฉันสงสัยว่าทำไม stackoverflow จึงต้องแมปเลขจำนวนเต็มขนาดใหญ่ที่สร้างขึ้นโดยไม่ซ้ำกันกับ UUID เนื่องจาก API ของมันส่งคืนเฉพาะจำนวนเต็มใหญ่เป็นสตริง จะใช้ UUID ที่ไหนถ้าไม่อยู่ใน API ดูเหมือนว่าเราควรเลือก UUID หรือ BIGINT? ทำไมกลยุทธ์แบบผสมผสานนี้ ยัง +1 สำหรับคำอธิบายที่ชัดเจนในคำตอบของคุณ
nishant

4
UUID V3 / V5 ได้รับการออกแบบมาเพื่อเมื่อคุณต้องการแปลงเนมสเปซที่มีอยู่ (และมีแนวโน้มที่จะชนกัน) โดยกำหนดให้เป็นเนมสเปซ UUID เดียวซึ่งมักมีประโยชน์เมื่อรวมชุดข้อมูล หากไม่ได้ผลกับสิ่งที่คุณกำลังทำอยู่ให้ใช้ V1 / V4
StephenS
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.