2023/7/10

UUID

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

沒有留言:

張貼留言