Các phiên bản UUID và khi nào nên dùng
(ntietz.com)- UUID có 8 phiên bản từ v1 đến v8; số cao hơn không có nghĩa là mới hơn hay tốt hơn, mà là các cách tạo khác nhau được định nghĩa trong RFC 9562
- Lựa chọn phổ biến thường thu hẹp vào v4 và v7: v4 là mặc định cho ID ngẫu nhiên, còn v7 phù hợp khi cần sắp xếp theo thời gian tạo
- v1 và v6 dùng cùng vật liệu đầu vào, nhưng v6 thay đổi thứ tự các trường để khi sắp xếp sẽ theo thời gian tạo
- v3 và v5 tạo UUID bằng cách băm dữ liệu đầu vào; v3 dùng MD5, v5 dùng SHA-1, và DNS, URL có thể là các giá trị đầu vào ứng viên
- Trên thực tế, nếu có thể thì dùng v7 thay cho v1/v6; nếu cần UUID dựa trên dữ liệu đầu vào thì dùng v5; nếu cần UUID hoàn toàn tùy chỉnh thì cân nhắc v8
Cách tạo theo từng phiên bản UUID
- Các phiên bản UUID từ v1 đến v8 đều được định nghĩa trong RFC 9562
-
UUID dựa trên thời gian
- UUID Version 1 được tạo từ timestamp, bộ đếm đơn điệu và địa chỉ MAC
- UUID Version 6 dùng cùng dữ liệu như v1, nhưng đổi thứ tự để khi sắp xếp sẽ theo thời gian tạo
- UUID Version 7 được tạo từ timestamp và dữ liệu ngẫu nhiên
-
UUID ngẫu nhiên và do người dùng định nghĩa
- UUID Version 4 được tạo hoàn toàn từ dữ liệu ngẫu nhiên, gần với hình dung của nhiều người khi nhắc đến UUID
- UUID Version 8 là cơ chế hoàn toàn do người dùng định nghĩa, ngoại trừ các trường version/variant cần có ở mọi phiên bản
-
UUID dựa trên băm dữ liệu đầu vào
- UUID Version 3 được tạo từ băm MD5 của dữ liệu do người dùng cung cấp
- Các giá trị đầu vào ứng viên trong RFC gồm DNS và URL
- UUID Version 5 được tạo từ băm SHA-1 của dữ liệu do người dùng cung cấp
- Tương tự v3, DNS và URL có thể được dùng làm đầu vào ứng viên
- UUID Version 3 được tạo từ băm MD5 của dữ liệu do người dùng cung cấp
-
UUID được dành riêng
- UUID Version 2 được dành riêng cho ID bảo mật và không có chi tiết đã biết
Tiêu chí lựa chọn trong thực tế
- Trong hầu hết trường hợp, lựa chọn là v4 hoặc v7
- Nếu chỉ cần một ID ngẫu nhiên, v4 là lựa chọn mặc định phù hợp
- Nếu ID cần có khả năng sắp xếp, có thể cân nhắc v7
- Ví dụ, khi dùng UUID làm khóa cơ sở dữ liệu, v7 có thể là một ứng viên
- v5 hoặc v8 phù hợp hơn khi muốn đưa dữ liệu riêng vào trong UUID
- Với các kiểu này, thường người dùng đã biết rõ mình cần chúng trong tình huống nào
- Theo RFC, v7 cải tiến v1 và v6, vì vậy nếu có thể nên dùng v7 thay cho v1/v6
- Nếu nhất thiết cần v1 hoặc v6, có thể dùng v6
- v2 được dành riêng cho mục đích bảo mật chưa được đặc tả
- v3 đã được thay thế bởi v5, phiên bản dùng hàm băm mạnh hơn; nếu cần v3 thì nhiều khả năng người dùng cũng đã biết rõ lý do
1 bình luận
Các ý kiến trên Hacker News
Nói rằng không có chi tiết về UUID v2 chỉ đúng nếu bạn chỉ đọc RFC vốn nổi tiếng là mơ hồ: https://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#t...
Hầu như bạn sẽ không gặp nó, nhưng cũng có UUID version 0. Nó đáng được nhắc đến vì là nguồn gốc của các bit dự trữ cho phép các “phiên bản” khác về sau được định nghĩa theo cách tương thích. Tôi đã tổng hợp phần khảo cứu liên quan trong thư viện UUID của mình: https://github.com/okeeblow/DistorteD/blob/NEW%E2%80%85SENSA...
Vì nó khá hay nên tôi quyết định hỗ trợ, nhưng vẫn còn phải quyết định cách xử lý rollover ngày tháng và các Apollo UID còn cũ hơn
Tôi đang bắt đầu làm một RFC mang tính thông tin về các kiểu UUID lịch sử được định nghĩa trong không gian Variant 0-7, nhằm giúp mọi người hiểu hơn. Nếu muốn thảo luận hoặc rà soát câu chữ, có thể xem https://github.com/yocto/draft-yocto-uuid
Có thể tìm thấy chi tiết trong 2 phút. Bấm vào liên kết trong bài để đến phần định nghĩa DCE của RFC 9562, theo liên kết đầu tiên trong đoạn đó để vào đặc tả, rồi tìm “UUID” để tới phụ lục A là có hết
Tên gọi hơi gây nhầm là “Universal Unique Identifier”, nhưng nội dung cần thiết đều có. Ít nhất cũng nên tự bấm thử các liên kết mình đưa vào bài
Xét theo ngữ cảnh, vì các phiên bản khác được mô tả là được tạo từ những yếu tố cụ thể, nên câu đó rõ ràng đang nói về ý thứ hai. Câu văn hơi không rõ, nhưng khó nói là nó dẫn dắt hiểu lầm
Dù vậy phụ lục đọc cũng thú vị, như một snapshot của thời điểm đó
Ước gì có một chuẩn UUID ngắn kiểu
73WakrfVbNJBaAmhQtEeDvhaybK7nP9xMNói nghiêm ngặt thì vì ở đâu đó vẫn có thể trùng nên không phải UUID, nhưng tôi muốn có một tổ hợp chuẩn cho ID vừa ngẫu nhiên vừa đủ ngắn để nhớ
Tôi nghĩ lý do không có chuẩn phổ biến hơn là vì phải đánh đổi điều gì đó. 128 bit có rủi ro va chạm thấp cho gần như mọi mục đích, nhưng càng nhỏ hơn thì càng phải xét tình huống cụ thể và tác động của va chạm, nên khó chuẩn hóa. Dùng các mã hóa khác như base64 hay base85 thì ngắn hơn, nhưng phải hy sinh những thứ như phân biệt hoa thường và độ an toàn trong URL: https://github.com/ulid/spec
Nó chỉ là một cách biểu diễn khác của cùng UUID và có thể chuyển ngược. Rốt cuộc UUID là một giá trị 128 bit, nên đây giống cách ghi thay thế hơn là chuyển đổi thực sự
Bạn sẽ mất lợi thế sắp xếp đơn điệu có trong một số phiên bản UUID, nhưng base58 an toàn cho URL và không chứa ký tự đặc biệt. Giá trị vẫn có thể được lưu dưới dạng nhị phân. Ví dụ trong Postgres có thể dùng
byteathay vì cột văn bảnCó thể xem thảo luận tại đây: https://github.com/uuid6/new-uuid-encoding-techniques-ietf-d...
Timestamp của UUID v7 là một thay đổi lớn với Databend. Chúng tôi dùng nó để nhanh chóng tìm các tệp metadata theo timestamp trên AWS S3, nên các tác vụ như vacuum nhanh hơn nhiều
PR: https://github.com/datafuselabs/databend/pull/16049
Lợi thế lớn của UUID sắp xếp theo thời gian là tính cục bộ tốt hơn. Khi thêm mục mới vào chỉ mục, phần lớn sẽ là thao tác nối vào cuối, có thể rẻ hơn so với chèn ngẫu nhiên. Tuy nhiên cũng có thể tăng tranh chấp, nên một phương án lai đáng cân nhắc là đặt một số bit ngẫu nhiên trước timestamp để tạo các “shard” có thứ tự. Việc đọc cũng thường tập trung vào dữ liệu mới nhất, nên dữ liệu mới nằm cùng một chỗ và dễ vào cache là điều hữu ích
Khó hiểu mục đích của uuid2. Tôi cũng không biết là còn có những kiểu như vậy, và lần đầu thấy uuid2 khi yêu cầu Xandr xóa dữ liệu cá nhân của mình: https://news.ycombinator.com/item?id=40913915
Đọc Wikipedia rồi vẫn không hiểu vì sao đã tạo ra cái gọi là “định danh duy nhất phổ quát” mà lại còn có nhiều kiểu, và vì sao một số trong đó ban đầu được thiết kế để có thể truy vết tới tận PC. Tôi thắc mắc liệu việc trộn một phần mã MAC có làm uuid2 gần với ngẫu nhiên hơn không, hay có lý do nào khác. Từ góc độ bảo vệ quyền riêng tư, tôi cũng thắc mắc liệu chỉ dùng một định danh dài với rất nhiều ký tự có thể chọn, để khả năng trùng lặp gần như bằng không, có được không
Bất kỳ hai máy nào cũng có thể tạo cùng một UID/UUID từ cùng hai đầu vào, và bên nhận thông điệp đã được định danh có thể chuyển định danh đó ngược lại thành các thành phần ban đầu. Vì được thiết kế làm nhãn cho các thông điệp tạm thời, hai chiều đó là thời gian và ID phần cứng; ban đầu dùng số sê-ri Apollo, về sau dùng địa chỉ phần cứng Ethernet, v.v.
Tôi cho rằng phần lớn sự nhầm lẫn đến từ việc trong triển khai AEGIS ban đầu, các kỹ sư Apollo bắt đầu dùng UID “canned”, tức tĩnh và đã biết rộng rãi, để định danh hệ thống tệp. Theo thời gian, mục đích sử dụng phổ biến của UUID đã chuyển hẳn từ định danh tạm thời trong đó trùng lặp là có chủ đích, sang định danh canned cần tránh trùng lặp; và hai chiều cũng trở thành ngẫu nhiên và lại ngẫu nhiên
Lịch sử còn phức tạp hơn. Microsoft đã thuê một trong những nhân vật chủ chốt của Apollo để làm MSRPC cho Windows NT, và vì thế GUID cũng ra đời. GUID có cách bố trí trường khác UUID, và trái với điều nhiều tài liệu nói, nó không phải mixed-endian. Microsoft rất thích dùng GUID canned không chỉ để định danh các thông điệp RPC tạm thời, mà còn cho gần như mọi thứ cần định danh đã biết rộng rãi như lớp COM, codec media, v.v. Ví dụ: https://gix.github.io/media-types/
Xin lỗi vì liên kết tới kho của tôi hai lần trong cùng một phần bình luận, nhưng tôi đã bắt đầu tổng hợp lịch sử này trong README của thư viện UUID của mình và cần tiếp tục lại. Apollo bắt đầu từ năm 1980, còn bản nháp RFC UUID của Leach/Salz mãi tới năm 1998 mới xuất hiện, nên có cực kỳ nhiều câu chuyện bị thiếu trong tiêu chuẩn hiện đại: https://github.com/okeeblow/DistorteD/blob/NEW%E2%80%85SENSA...
UUID v4 chỉ là bộ sinh byte ngẫu nhiên có chèn dấu gạch nối ở các vị trí được quy định. Không nhất thiết phải dùng nó; tự tạo byte ngẫu nhiên có thể tiết kiệm dung lượng
Cũng giảm được các thứ không cần thiết như dấu gạch nối, thông tin phiên bản
Tương tự như địa chỉ IPv4 là số 32 bit, còn “bốn cụm phân tách bằng dấu chấm” chỉ là một trong các cách biểu diễn. Nếu bạn nghĩ UUID là một định dạng chuỗi, thì ngay từ khái niệm cơ bản nhất về UUID bạn đã hiểu sai rồi. Ngay cả khi chỉ muốn một định danh ngẫu nhiên, tôi vẫn thấy việc UUID ngẫu nhiên có một vài bit cờ nhỏ nói rằng “cái này được chủ ý tạo ngẫu nhiên” là hay. Nó hữu ích khi bạn chỉ tìm thấy một định danh không có ngữ cảnh
Có thể tranh luận việc tạo các không gian tên khác nhau giữa nhiều cách sinh có hữu ích hay không, nhưng xác suất một bộ sinh số ngẫu nhiên thông thường tạo ra UUIDv4 hợp lệ chỉ là 1/16. Tất nhiên nếu muốn tự viết bộ sinh UUID thì đặt đúng các bit là chuyện nhỏ
Với Go thì chỉ cần
uuid.New().String(), còn nếu đọc dữ liệu ngẫu nhiên bằngcrypto/randrồi chuyển sang base64 hoặc hex thì cần nhiều dòng và công sức hơnKhuyến nghị không dùng các phiên bản dựa trên MAC. Về lý thuyết có thể bao gồm mọi phiên bản ngoài v4 và v7, nhưng v1 là tệ nhất
v3 cũng có vấn đề là MD5 đã bị phá vỡ nghiêm trọng
Tôi không biết các chi tiết ngoài số 4, nhưng thứ thật sự hữu ích còn thiếu có lẽ là cách dùng dữ liệu SHA256 và bộ đếm. Nó có dạng tương tự PBKDF2
Có thể trở thành định danh phái sinh vẫn bảo toàn quyền riêng tư, và cũng có thể chứng minh một cách lỏng lẻo rằng một UUID cụ thể được phái sinh từ seed nào đó
Ngoài ra thì có lẽ bạn sẽ muốn đầu ra dài hơn
Cứ dùng v7 là được
Giờ đến lượt các chuyên gia bảo mật nói là không phải vậy
Với nhiều trường hợp sử dụng, sẽ rất hữu ích nếu xử lý lại dữ liệu vẫn tạo ra cùng một ID, nhưng tôi không biết cách chuẩn nào để đạt được điều đó