Mục lục
UUID (Universally Unique Identifier) hay còn được gọi là GUID (Globally Unique Identifier) đại diện cho một giá trị cho độ dài 128bit. Để biễu diễn UUID chúng ta thường sử dụng hệ hex (octets) có dạng như sau:
123e4567-e89b-12d3-a456-556642440000
Một UUID được tạo thành bởi các chữ số hệ hex đặt xen kẽ bởi ký tự gạch ngang “-” có độ dài bằng 36 ký tự.
Nill UUID là một trường hợp đặc biệt, trong đó toàn bộ các bit đều được gán thành 0.
00000000-0000-0000-0000-000000000000
Trong bài viết nàym chúng ta sẽ cùng nhau tìm hiểu UUID class được hỗ trợ sẵn trong Java. Đầu tiên chúng ta sẽ tìm hiểu cách sử dụng class này, và sau đó sẽ tìm hiểu qua những phiên bản trong quá khứ của UUID để hiểu rõ hơn về quá trình phát triển của nó.
UUID class
UUID class chỉ có duy nhất 1 constructor”:
UUID uuid = new UUID(long mostSignificant64Bits, long leastSignificant64Bits);
Nếu muốn sử dụng constructor này thì chúng ta phải cung cấp đến 2 giá trị có kiểu long. Nên chúng ta có thể chuyển sang sử dụng static method được cung cấp sẵn để tạo một UUID object.
UUID uuid = UUID.nameUUIDFromBytes(byte[] bytes);
Method này giúp chúng ta tạo ra một UUID object thông qua một mảng byte. Cách này cũng khá phiền phức, nền chúng ta có thể sử dụng một method khác không yêu cầu truyền bất kỳ một tham số nào.
UUID uuid = UUID.randomUUID();
Được ra đời từ phiên bản UUID 4, randomUUID() là method được sử dụng nhiều nhất để tạo ra một UUID object vì tính tiện lợi và nhanh chóng của nó.
Ngoài ra còn một static method có thể tạo ra một UUID object thông qua một String truyền vào.
UUID uuid = UUID.fromString(String uuidHexDigitString);
Cấu trúc của một UUID
Hãy xem ví dụ về một chuỗi UUID sau:
123e4567-e89b-42d3-a456-556642440000 // Chú ý các ký tự A, B xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
Trong đó 4bit M và từ bit thứ 1 đến bit thứ 3 trong N dùng để định nghĩa toàn bộ cấu trúc của UUID.
Thuộc tính variant được dùng để đại diện cho định dạng và mã hoá của UUID. Có 4 loại variant được xác theo cách sau:
MSB1 MSB2 MSB3 0 X X reserved (0) 1 0 X current variant (2) 1 1 0 reserved for Microsoft (6) 1 1 1 reserved for future (7)
Như trong ví dụ của chúng ta, M là 1 và N là ‘a’ (tương ứng với 10xx ở hệ 2). Có nghĩa là UUID này có version là 1 và variant là 1.
Để hiễu rõ thêm, chúng ta có thể tìm hiểu sơ qua ý nghĩa của 4 loại variant trên như sau:
- variant – 0: dùng để tương thích ngược lại định dạng UUID của hệ thống máy tính Apollo Network Computing System 1.5 UUID được thiết kế vào năm 1998, hiện đã lỗi thời. 6 octet đầu tiên của UUID là dấu thời gian 48 bit (số đơn vị thời gian 4 micro giây kể từ ngày 1 tháng 1 năm 1980 theo giờ UTC. 2 octet tiếp theo được dành riêng; octet tiếp theo là “address family; và 7 octet cuối cùng là một ID máy chủ 56 bit ở dạng được chỉ định bởi address family.
- variant – 1: được gọi là UUID RFC 4122 / DCE 1.1, hoặc UUID “Leach – Salz”, theo tên tác giả của Bản nháp Internet gốc.
- variant – 2: đặc trưng trong RFC là “dành riêng Microsoft Corporation” và được sử dụng cho các GUID ban đầu trên nền tảng Microsoft Windows.
- variant – 3: được định nghĩa là mẫu bit biến thể 3-bit 111×2 (N = e..f).
UUID version
UUID version 1
UUID version 1 dựa trên current timestamp được đo bằng đơn vị 100 nano giây từ ngày 15 tháng 10 năm 1582, được nối với địa chỉ MAC của thiết bị nơi UUID được tạo.
Nếu lo ngại về vấn đề riêng tư, UUID version 1 có thể được tạo bằng số 48 bit ngẫu nhiên thay vì địa chỉ MAC của máy tính chúng ta.
Để tạo ra UUID version 1, trước tiên chúng ta cần tạo ra 64 bit least và 64 bit most significant
private static long get64LeastSignificantBitsForVersion1() { Random random = new Random(); long random63BitLong = random.nextLong() & 0x3FFFFFFFFFFFFFFFL; long variant3BitFlag = 0x8000000000000000L; return random63BitLong + variant3BitFlag; } private static long get64MostSignificantBitsForVersion1() { LocalDateTime start = LocalDateTime.of(1582, 10, 15, 0, 0, 0); Duration duration = Duration.between(start, LocalDateTime.now()); long seconds = duration.getSeconds(); long nanos = duration.getNano(); long timeForUuidIn100Nanos = seconds * 10000000 + nanos * 100; long least12SignificatBitOfTime = (timeForUuidIn100Nanos & 0x000000000000FFFFL) >> 4; long version = 1 << 12; return (timeForUuidIn100Nanos & 0xFFFFFFFFFFFF0000L) + version + least12SignificatBitOfTime; }
Sau đó sử dụng chúng để tạo UUID thông qua constructor
public static UUID generateType1UUID() { long most64SigBits = get64MostSignificantBitsForVersion1(); long least64SigBits = get64LeastSignificantBitsForVersion1(); return new UUID(most64SigBits, least64SigBits); }
UUID version 2
Phiên bản 2 cũng dựa trên timestamp và địa chỉ MAC. Tuy nhiên, RFC 4122 không chỉ định chi tiết thế hệ chính xác, do đó, chúng ta sẽ không triển khai ví dụ trong phiên bản này.
UUID version 3 & 5
UUID được tạo bằng cách sử dụng hàm băm của namespace và name. Định danh vùng tên là UUID như Hệ thống tên miền (DNS), Object Identifiers (OID), URL, v.v.
UUID = hash(NAMESPACE_IDENTIFIER + NAME)
Sự khác biệt duy nhất giữa UUIDv3 và UUIDv5 là Thuật toán băm – v3 sử dụng MD5 (128 bit) trong khi v5 sử dụng SHA-1 (160 bit).
Nói một cách đơn giản, chúng tôi cắt ngắn kết quả băm thành 128-bit và sau đó thay thế 4 bit cho phiên bản và 2 bit cho variant.
Dưới đây là ví dụ để sinh ra một UUID version 3
byte[] nameSpaceBytes = bytesFromUUID(namespace); byte[] nameBytes = name.getBytes("UTF-8"); byte[] result = joinBytes(nameSpaceBytes, nameBytes); UUID uuid = UUID.nameUUIDFromBytes(result);
Ở đây, điều quan trọng cần lưu ý là chuỗi hex cho không gian tên trước tiên cần được chuyển đổi thành mảng byte. Lưu ý Java không hỗ trợ triển khai UUID version 5.
UUID version 4
UUID 4 sử dụng một số ngẫu nhiên để sinh ra một UUID.
UUID uuid = UUID.randomUUID();
Hãy tạo một khóa duy nhất bằng cách sử dụng SHA-256 và một UUID object.
MessageDigest salt = MessageDigest.getInstance("SHA-256"); salt.update(UUID.randomUUID().toString().getBytes("UTF-8")); String digest = bytesToHex(salt.digest());
Nguồn