Tạo UUID v5. Tên và không gian tên là gì?


125

Tôi đã đọc mantrang, nhưng tôi không hiểu những gì namenamespacedành cho.

Đối với UUID phiên bản 3 và phiên bản 5, không gian tên và tên đối số dòng lệnh bổ sung phải được cung cấp. Không gian tên là UUID trong biểu diễn chuỗi hoặc là số nhận dạng cho các UUID không gian tên được xác định trước nội bộ (hiện được biết đến là "ns: DNS", "ns: URL", "ns: OID" và "ns: X500"). Tên là một chuỗi có độ dài tùy ý.

Không gian tên:

Không gian tên là UUID trong biểu diễn chuỗi hoặc

Có nghĩa là tôi cần lưu trữ nó (UUID v4) ở đâu đó liên quan đến UUID v5 đã tạo? Trong cả hai trường hợp, tại sao điều này không được thực hiện tự động?

Tên là một chuỗi có độ dài tùy ý.

namemột chuỗi hoàn toàn ngẫu nhiên? Mục đích của nó sau đó là gì? Nó có thể được giải mã từ UUID v5 không?

Câu trả lời:


106

Tên và không gian tên có thể được sử dụng để tạo một hệ thống phân cấp của (rất có thể) UUID duy nhất.

Nói một cách đại khái, UUID loại 3 hoặc loại 5 được tạo ra bằng cách băm cùng một định danh không gian tên với một tên. UUID loại 3 sử dụng MD5 và UUID loại 5 sử dụng SHA1. Chỉ có 128 bit khả dụng và 5 bit được sử dụng để chỉ định loại, vì vậy tất cả các bit băm không được đưa vào UUID. (Ngoài ra MD5 được coi là bị phá vỡ mật mã và SHA1 đang ở chân cuối cùng của nó, vì vậy không sử dụng điều này để xác minh dữ liệu cần phải "rất an toàn"). Điều đó nói rằng, nó cung cấp cho bạn cách tạo một hàm "băm" có thể lặp lại / có thể xác minh ánh xạ một tên có thể phân cấp thành một giá trị 128-bit duy nhất về mặt xác suất, có khả năng hoạt động giống như một băm phân cấp hoặc MAC.

Giả sử bạn có một cửa hàng (khóa, giá trị), nhưng nó chỉ hỗ trợ một không gian tên. Bạn có thể tạo một số lượng lớn không gian tên logic riêng biệt bằng cách sử dụng UUID loại 3 hoặc loại 5. Đầu tiên, tạo một UUID gốc cho mỗi không gian tên. Đây có thể là UUID loại 1 (máy chủ + dấu thời gian) hoặc loại 4 (ngẫu nhiên), miễn là bạn cất nó ở đâu đó. Ngoài ra, bạn có thể tạo một UUID ngẫu nhiên cho root của mình (hoặc sử dụng nullUUID: 00000000-0000-0000-0000-000000000000as root) và sau đó tạo một UUID có thể tái tạo cho mỗi vùng tên bằng cách sử dụng " uuid -v5 $ROOTUUID $NAMESPACENAME". Giờ đây, bạn có thể tạo UUID duy nhất cho các khóa trong không gian tên bằng cách sử dụng "uuid -v5 $NAMESPACEUUID $KEY". Các UUID này có thể được ném vào một kho khóa-giá trị duy nhất với khả năng tránh va chạm cao. Quá trình này có thể được lặp lại một cách đệ quy để nếu chẳng hạn" giá trị "được liên kết với khóa UUID đại diện cho một số loại không gian tên" logic "như một thùng, vùng chứa hoặc thư mục, thì UUID của nó có thể được sử dụng lần lượt để tạo ra nhiều UUID phân cấp hơn.

UUID loại 3 hoặc loại 5 được tạo chứa hàm băm (một phần) của id vùng tên và tên trong vùng tên (khóa). Nó không giữ UUID không gian tên hơn là một thông điệp MAC giữ nội dung của thông điệp mà nó được mã hóa. Tên là một chuỗi "tùy ý" (octet) theo quan điểm của thuật toán uuid. Tuy nhiên, ý nghĩa của nó phụ thuộc vào ứng dụng của bạn. Nó có thể là một tên tệp trong một thư mục logic, id đối tượng trong một cửa hàng đối tượng, v.v.

Mặc dù điều này hoạt động tốt với một số lượng lớn không gian tên và khóa vừa phải, nhưng cuối cùng nó sẽ hết sạch nếu bạn đang nhắm đến một số lượng rất lớn các khóa là duy nhất với xác suất rất cao. Mục nhập Wikipedia về Vấn đề sinh nhật (hay còn gọi là Nghịch lý sinh nhật) bao gồm một bảng đưa ra xác suất của ít nhất một vụ va chạm đối với số lượng khóa và kích thước bảng khác nhau. Đối với 128 bit, băm 26 tỷ khóa theo cách này có xác suất va chạm là p=10^-18(không đáng kể), nhưng 26 nghìn tỷ khóa, làm tăng xác suất của ít nhất một lần va chạm lên p=10^-12(một trong một nghìn tỷ) và băm 26*10^15các khóa, làm tăng xác suất ít nhất một vụ va chạm vớip=10^-6(một trong một triệu). Điều chỉnh cho 5 bit mã hóa loại UUID, nó sẽ hết nhanh hơn một chút, vì vậy một nghìn tỷ khóa có khả năng xảy ra một vụ va chạm duy nhất là 1 nghìn tỷ.

Xem http://en.wikipedia.org/wiki/Birthday_problem#Probability_table để biết bảng xác suất.

Xem http://www.ietf.org/rfc/rfc4122.txt để biết thêm chi tiết về mã hóa UUID.


2
Ở một cấp độ nhất định trong hệ thống phân cấp, tôi có thể sử dụng UUIDv5 làm không gian tên và UUIDv4 làm khóa ngẫu nhiên để đảm bảo các xung đột trong chính dữ liệu (đang được nhận dạng bởi GUID này) không làm tăng khả năng va chạm các UUID không? Bất kỳ vấn đề hiệu suất nào tôi nên biết?
ermik

Tôi mới làm quen với khái niệm này và không hiểu hệ thống phân cấp mà bạn đang nói đến là gì. Tôi có thể nhìn thấy nó ở đâu vv ... Một số rõ ràng đến một lần tôi bị mắc kẹt trên giải thích điều này có thể được sử dụng để tạo ra một UUID tái sản xuất cho không gian tên . Tôi tự hỏi có cách nào để xác minh rằng một UUID nhất định (thuộc loại 3 hoặc 5) đã được tạo bằng cách sử dụng một không gian tên cụ thể (UUID của nó) không?
msciwoj

213

UUID loại 3 và loại 5 chỉ là một kỹ thuật nhồi một hàm băm vào một UUID.

Hàm băm SHA1 xuất ra 160 bit (20 byte); kết quả của băm được chuyển đổi thành UUID.

Với hàm băm 20 byte từ 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

(Lưu ý rằng hai bit đầu tiên của '9' đã là 1 và 0, vì vậy điều này không có tác dụng).

Tôi băm cái gì?

Có lẽ bạn đang thắc mắc rằng tôi phải băm là gì. Về cơ bản, bạn băm sự kết hợp của:

sha1([NamespaceUUID]+[AnyString]);

Bạn đặt trước chuỗi của mình bằng một cái gọi là không gian tên để ngăn xung đột tên.

Các UUID RFC Pre-xác định bốn không gian tên cho bạn:

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

Vì vậy, bạn có thể băm cùng nhau:

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

Sau đó RFC xác định cách:

  • lấy 160 bit từ SHA1
  • và chuyển đổi nó thành 128 bit của UUID

Ý chính cơ bản là chỉ lấy 128 bit đầu tiên, nhét a 5vào bản ghi kiểu , sau đó đặt hai bit đầu tiên của clock_seq_hi_and_reservedphần thành 1 và 0 tương ứng.

Thêm ví dụ

Bây giờ bạn có một hàm tạo ra cái gọi là Tên , bạn có thể có hàm (ở dạng mã giả):

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

(Lưu ý rằng sự cố kết thúc của hệ thống của bạn có thể ảnh hưởng đến các chỉ số của các byte trên)

Bạn có thể có các cuộc gọi:

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

Bây giờ trở lại câu hỏi của bạn

Đối với UUID phiên bản 3 và phiên bản 5, không gian tên và tên đối số dòng lệnh bổ sung phải được cung cấp. Không gian tên là UUID trong biểu diễn chuỗi hoặc là số nhận dạng cho các UUID không gian tên được xác định trước nội bộ (hiện được biết đến là "ns: DNS", "ns: URL", "ns: OID" và "ns: X500"). Tên là một chuỗi có độ dài tùy ý.

Không gian tên là bất kỳ UUID nào bạn thích. Nó có thể là một trong những cái được xác định trước, hoặc bạn có thể tự tạo, ví dụ:

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

Tên là một chuỗi có độ dài tùy ý.

Tên chỉ là văn bản bạn muốn thêm vào không gian tên, sau đó được băm và nhồi vào một 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');

Lưu ý : Bất kỳ mã nào được phát hành vào miền công cộng. Không cần ghi công.


45
Cảm ơn vì đã giải thích cặn kẽ. Nếu tôi có thể cho điểm thưởng cho Namespace_RectalForeignExtractedObjecttôi.
boodle

Có thể giải mã tên hoặc không gian tên được giải mã từ UUID không?
Sathesh

3
@Sathesh Không, không thể giải mã hàm băm; hàm băm là hàm một chiều. Ví dụ: toàn bộ bộ sưu tập Star Trek TNG Blu-Ray là 81 GB và có băm là C5740BBBF2429115276D4AB60A020ED3ADE01192 . Không có cách nào để giải mã hàm băm 20 byte đó trở lại thành 81 GB. Nếu bạn thực sự cần thiết, bạn có thể thử băm tất cả các GUID có thể có và các chuỗi có thể có cho đến khi bạn tìm thấy kết hợp cho cùng một kết quả. Với bất kỳ luch nào, bạn sẽ tìm thấy nó ở đâu đó giữa vĩnh viễn và vĩnh cửu.
Ian Boyd

22

Tên không khác gì một mã định danh duy nhất trong một số không gian tên. Vấn đề là không gian tên thường khá nhỏ và tên trong một vùng này thường xung đột với tên của những vùng khác. Ví dụ: biển số xe của tôi (tên) là duy nhất trong không gian tên của DMV tiểu bang của tôi, nhưng nó có lẽ không phải là duy nhất trên thế giới; các DMV của tiểu bang khác có thể đã sử dụng cùng một tên trong không gian tên riêng của họ. Rất tiếc, ai đó có thể có số điện thoại (tên) cũng khớp vì đó là không gian tên khác, v.v.

UUID có thể được coi là nơi sinh sống của một không gian tên duy nhất rộng lớn đến mức nó có thể cung cấp một tên duy nhất cho mọi thứ ; đó là những gì "phổ quát" có nghĩa là. Nhưng làm cách nào để ánh xạ các tên hiện có trong các không gian tên khác với một UUID?

Một giải pháp rõ ràng là tạo một UUID (V1 hoặc V4) cho mọi mục để thay thế các tên cũ trong không gian tên riêng biệt của chúng. Nhược điểm là chúng lớn hơn rất nhiều, bạn phải thông báo tất cả các tên mới cho tất cả những người có bản sao tập dữ liệu của bạn, cập nhật tất cả các API của bạn, v.v. Tỷ lệ cược là bạn không thể thực sự loại bỏ hoàn toàn các tên cũ Dù sao đi nữa, có nghĩa là bây giờ mọi mặt hàng đều có hai tên, vậy bạn đã làm cho mọi thứ tốt hơn hay tệ hơn?

Đây là nơi V3 / V5 xuất hiện. Các UUID trông cũng ngẫu nhiên như V4 nhưng thực sự là xác định; bất kỳ ai có UUID phù hợp cho không gian tên sau đó có thể độc lập tạo cùng một UUID cho bất kỳ tên cụ thể nào trong không gian tên đó. Bạn không cần phải xuất bản chúng hoặc thậm chí tạo trước chúng vì bất kỳ ai cũng có thể tạo chúng ngay khi cần!

Tên DNS và URL là không gian tên được sử dụng rất phổ biến, vì vậy các UUID tiêu chuẩn đã được xuất bản cho những không gian đó; Các tên ASN.1 OID và X.500 không phổ biến, nhưng các cơ quan tiêu chuẩn yêu thích chúng, vì vậy họ cũng xuất bản các UUID không gian tên tiêu chuẩn cho chúng.

Đối với tất cả các không gian tên khác, bạn phải tạo UUID không gian tên của riêng mình (V1 hoặc V4) và giao tiếp nó với bất kỳ ai cần. Nếu bạn có nhiều không gian tên, việc phải xuất bản UUID cho từng không gian tên rõ ràng là không lý tưởng.

Đây là lúc hệ thống phân cấp xuất hiện: bạn tạo một UUID "cơ sở" (thuộc bất kỳ loại nào), và sau đó sử dụng nó làm không gian tên để đặt tên cho các không gian tên khác của bạn! Bằng cách đó, bạn chỉ phải xuất bản UUID cơ sở (hoặc sử dụng một UUID rõ ràng) và mọi người có thể tính toán phần còn lại.

Ví dụ: hãy ở lại, chúng tôi muốn tạo một số UUID cho StackOverflow; có tên rõ ràng trong không gian tên DNS, vì vậy cơ sở là rõ ràng:

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

Bản thân StackOverflow có các không gian tên riêng biệt cho người dùng, câu hỏi, câu trả lời, nhận xét, v.v., nhưng chúng cũng khá rõ ràng:

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

Câu hỏi cụ thể này là # 10867405, vì vậy UUID của nó sẽ là:

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

Lưu ý rằng không có ngẫu nhiên trong quá trình này, vì vậy bất kỳ ai tuân theo cùng một logic sẽ nhận được câu trả lời giống nhau, tuy nhiên không gian tên UUID rất rộng lớn đến mức nó sẽ (một cách hiệu quả, do tính bảo mật của băm mật mã 122 bit) không bao giờ va chạm với UUID được tạo từ bất kỳ cặp tên / không gian tên nào khác.


Tôi đang tự hỏi tại sao stackoverflow cần ánh xạ một số nguyên lớn được tạo duy nhất tới một UUID cho rằng các API của nó dường như chỉ trả về số nguyên lớn dưới dạng một chuỗi. UUID sẽ được sử dụng ở đâu nếu không có trong API. Có vẻ như chúng ta nên chọn UUID hoặc BIGINT? Tại sao lại thực hiện chiến lược lai này. Tuy nhiên, hãy +1 để có lời giải thích rõ ràng trong câu trả lời của bạn.
nishant

4
UUID V3 / V5 được thiết kế cho khi bạn cần chuyển đổi một cách rõ ràng các vùng tên hiện có (và có thể xảy ra xung đột) sang một vùng tên UUID, điều này thường hữu ích khi hợp nhất các tập dữ liệu. Nếu điều đó không áp dụng cho những gì bạn đang làm, thì hãy sử dụng V1 / V4.
StephenS
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.