UUID: Universally Unique Identifier 通用唯一識別碼,是 128 位元的識別碼,被開源軟體基金會 (Open Software Foundation, OSF) 的組織使用在分散式計算環境 (Distributed Computing Environment, DCE)。用途是讓分散式系統中的元素,能有唯一的辨識資訊,不需要透過一個集中的系統辨識資訊的唯一性。2005年7月,RFC4122 定義了 UUID 的 URN name space,並制定 UUID 的規格。
結構
UUID 的結構為 16 個 8 bits 的 byte array,總共是 32 個 16 進位的數字,以 "8-4-4-4-12" 的形式表示
xxxxxxxx-xxxx-Bxxx-Axxx-xxxxxxxxxxxx
Variant
其中 A 代表 UUID variants 欄位,目前的規格使用 variant 2
Name |
binary bits |
description |
Apollo NCS Variant |
0XXX |
1988 年 Apollo 系統使用 |
OSF DCE Variant |
10XX |
RFC 4122/DCE 1.1 UUIDs |
Microsoft COM/DCOM Variant |
110X |
reserved, Microsoft Corporation backward compatibility |
Reserved Variant |
未定義 |
所有未定義的格式 |
B 代表 Version,以一整個 byte 的值代表 UUID 的版本
目前 Version 有 1,2,3,4,5 五種,最常使用的是隨機生成的 Version 4。
- Time-Based (UUIDv1)
- DCE Security (UUIDv2)
- Name Based (UUIDv3 and UUIDv5)
- Random (UUIDv4)
Version
Version 1
根據時間, Version, Variant 產生前四個部分的值,根據 Mac Address 產生 Node。這個版本的 UUID 優點是依照時間順序產生,因此排序速度比較快,但也比較有可能會產生相同的 UUID。
如果是真實的 MAC Address,可以追蹤到原始產生 UUID 的電腦,但這部分也可以用亂數產生。
98a956f7-0188-1000-88fc-6650c08fc392
98a956f7: Low Time
0188: Mid Time
1000: High Time and Version
88fc: Clock Sequence and Variant
6650c08fc392: Node
Version 2
比較少被使用,規格定義於 DCE 1.1: Authentication and Security Services - Privilege (Authorisation) Services
Version 3, 5
根據 namespace 及唯一的 name 產生,當輸入的字串固定時,產生的 UUID 也會是固定的。
Version 3 是使用 MD5 hash of the name and namespace
Version 5 是使用 SHA-1 hash of the name and namespace,當 hash 結果長度太長時,就直接 truncated
Version 4
完全是亂數產生的,有超過 5.3 x 10^36 個 UUIDs,這是最常被使用的版本
Java Code
import java.util.Random;
import java.util.UUID;
public class UUIDTest {
public static void main(String... args) {
v1();
v3();
v4();
}
private static void uuid_field(UUID uuid1) {
int version = uuid1.version();
int variant = uuid1.variant();
int clockSequence = -1;
long node = -1;
long timestamp = -1;
try {
clockSequence = uuid1.clockSequence();
node = uuid1.node();
timestamp = uuid1.timestamp();
} catch(UnsupportedOperationException e) {
}
System.out.println("uuid="+uuid1+", version="+version+", variant="+variant+", clockSequence="+clockSequence+", node="+node+", timestamp="+timestamp);
}
private static void v4() {
UUID uuid1 = UUID.randomUUID();
UUID uuid2 = UUID.randomUUID();
UUID uuid3 = UUID.randomUUID();
System.out.println("UUID v4");
uuid_field(uuid1);
uuid_field(uuid2);
uuid_field(uuid3);
System.out.println("");
}
private static void v3() {
String s1 = "Test String";
String s3 = "Test String3";
UUID uuid1 = UUID.nameUUIDFromBytes(s1.getBytes());
UUID uuid2 = UUID.nameUUIDFromBytes(s1.getBytes());
UUID uuid3 = UUID.nameUUIDFromBytes(s3.getBytes());
System.out.println("UUID v3");
uuid_field(uuid1);
uuid_field(uuid2);
uuid_field(uuid3);
System.out.println("");
}
// UUID.java 裡面的 v3 實作
// public static UUID nameUUIDFromBytes(byte[] name) {
// MessageDigest md;
// try {
// md = MessageDigest.getInstance("MD5");
// } catch (NoSuchAlgorithmException nsae) {
// throw new InternalError("MD5 not supported", nsae);
// }
// byte[] md5Bytes = md.digest(name);
// md5Bytes[6] &= 0x0f; /* clear version */
// md5Bytes[6] |= 0x30; /* set to version 3 */
// md5Bytes[8] &= 0x3f; /* clear variant */
// md5Bytes[8] |= 0x80; /* set to IETF variant */
// return new UUID(md5Bytes);
// }
private static void v1() {
UUID uuid1 = generateUUID_V1();
UUID uuid2 = generateUUID_V1();
UUID uuid3 = generateUUID_V1();
System.out.println("UUID v1");
uuid_field(uuid1);
uuid_field(uuid2);
uuid_field(uuid3);
System.out.println("");
}
public static UUID generateUUID_V1() {
long most64SigBits = get64MostSignificantBitsForVersion1();
long least64SigBits = get64LeastSignificantBitsForVersion1();
return new UUID(most64SigBits, least64SigBits);
}
private static long get64LeastSignificantBitsForVersion1() {
Random random = new Random();
long random63BitLong = random.nextLong() & 0x3FFFFFFFFFFFFFFFL;
long variant3BitFlag = 0x8000000000000000L;
return random63BitLong | variant3BitFlag;
}
private static long get64MostSignificantBitsForVersion1() {
final long currentTimeMillis = System.currentTimeMillis();
final long time_low = (currentTimeMillis & 0x0000_0000_FFFF_FFFFL) << 32;
final long time_mid = ((currentTimeMillis >> 32) & 0xFFFF) << 16;
final long version = 1 << 12;
final long time_hi = ((currentTimeMillis >> 48) & 0x0FFF);
return time_low | time_mid | version | time_hi;
}
}
執行結果
UUID v1
uuid=98a956f7-0188-1000-88fc-6650c08fc392, version=1, variant=2, clockSequence=2300, node=112497014064018, timestamp=1686188414711
uuid=98a956fb-0188-1000-b9c6-84e9d3083144, version=1, variant=2, clockSequence=14790, node=146139802775876, timestamp=1686188414715
uuid=98a956fb-0188-1000-9bdc-540d86968ed3, version=1, variant=2, clockSequence=7132, node=92417069321939, timestamp=1686188414715
UUID v3
uuid=bd08ba3c-982e-3ad7-a860-2536fb8e1184, version=3, variant=2, clockSequence=-1, node=-1, timestamp=-1
uuid=bd08ba3c-982e-3ad7-a860-2536fb8e1184, version=3, variant=2, clockSequence=-1, node=-1, timestamp=-1
uuid=c1a59283-7a55-3e83-b9ed-864b5367685b, version=3, variant=2, clockSequence=-1, node=-1, timestamp=-1
UUID v4
uuid=2de4d27e-9abc-4b9d-83bd-b721ef6b2882, version=4, variant=2, clockSequence=-1, node=-1, timestamp=-1
uuid=e82fca87-acd0-4000-8a4f-f4d7001d56a4, version=4, variant=2, clockSequence=-1, node=-1, timestamp=-1
uuid=fb3a6890-57e3-4a13-a238-ef6f3de63fbe, version=4, variant=2, clockSequence=-1, node=-1, timestamp=-1
References
通用唯一辨識碼 - 維基百科,自由的百科全書
# 閒談軟體架構:UUID
# 閒談軟體架構:UUID 之三部曲
Guide to UUID in Java | Baeldung