1. 程式人生 > 實用技巧 >【Mybatis工具(四)】UUID實現原理及其Java實現

【Mybatis工具(四)】UUID實現原理及其Java實現

UUID(Universally Unique Identifier),翻譯為中文是通用唯一識別碼,UUID 的目的是讓分散式系統中的所有元素都能有唯一的識別資訊。如此一來,每個人都可以建立不與其它人衝突的 UUID,就不需考慮資料庫建立時的名稱重複問題。UUID 是由一組32位數的16進位制數字所構成,是故 UUID 理論上的總數為1632=2128,約等於3.4 x 10123。也就是說若每納秒產生1百萬個 UUID,要花100億年才會將所有 UUID 用完。

格式

UUID 的128bit數字被表示為32個十六進位制數字,以連字號分隔的五組來顯示,形式為8-4-4-4-12,總共有 36個字元(即三十二個英數字母和四個連字號)。例如:
123e4567-e89b-12d3-a456-426655440000

xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
到目前為止業界一共有5種方式生成UUID,這5種方式使用不同演算法,利用不同的資訊來產生UUID,各版本有各自優勢,適用於不同情景。其中,數字M的4bit數字表示 UUID 版本,可選值為1, 2, 3, 4, 5對應當前規範的5個版本。

為了能相容過去的UUID,以及應對未來的變化,因此有了變體(Variants)這一概念。目前已知的變體有如下幾種:(其中字母“x”表示一個“不關心”值)
variant 0:0xxx。為了向後相容預留。
variant 1:10xx。當前正在使用的。
variant 2:110x。為早期微軟GUID預留。
variant 3:111x。為將來擴充套件預留。目前暫未使用。

其中,數字N的的4bit數字表示 UUID 變體( variant ),根據規定目前使用的variant有固定的兩位10xx,因此只可能取值8, 9, a, b

有關UUID格式,詳情見IETF釋出的UUID規範A Universally Unique IDentifier (UUID) URN Namespace

版本介紹

  • version 1, date-time & MAC address

基於時間戳及MAC地址的UUID實現。它包括了48位的MAC地址和60位的時間戳。

  • version 2, date-time & group/user id

這是最神祕的版本,RFC沒有提供具體的實現細節,以至於大部分的UUID庫都沒有實現它,只在特定的場景(DCE security)才會用到。所以絕大數情況,我們也不會碰到它。

  • version 3, MD5 hash & namespace
  • version 5, SHA-1 hash & namespace

V3和V5都是通過hash namespace的識別符號和名稱生成的。V3使用MD5作為hash函式,V5則使用SHA-1。

 /**
     * Static factory to retrieve a type 3 (name based) {@code UUID} based on
     * the specified byte array.
     *
     * @param  name
     *         A byte array to be used to construct a {@code UUID}
     *
     * @return  A {@code UUID} generated from the specified array
     */
    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);
    }
  • version 4, pseudo-random number

這個版本使用最為廣泛,其中4位代表版本,2-3位代表variant。餘下的122-121位都是全部隨機的。即有2的122次方(5.3後面36個0)個UUID。一個標準實現的UUID庫在生成了2.71萬億個UUID會產生重複UUID的可能性也只有50%的概率。這相當於每秒產生10億的UUID,持續85年,而把這些UUID都存入檔案,每個UUID佔16bytes,總需要45 EB(exabytes),比目前最大的資料庫(PB)還要大很多倍。

/**
     * Static factory to retrieve a type 4 (pseudo randomly generated) UUID.
     *
     * The {@code UUID} is generated using a cryptographically strong pseudo
     * random number generator.
     *
     * @return  A randomly generated {@code UUID}
     */
    public static UUID randomUUID() {
        SecureRandom ng = Holder.numberGenerator;

        byte[] randomBytes = new byte[16];
        ng.nextBytes(randomBytes);
        randomBytes[6]  &= 0x0f;  /* clear version        */
        randomBytes[6]  |= 0x40;  /* set to version 4     */
        randomBytes[8]  &= 0x3f;  /* clear variant        */
        randomBytes[8]  |= 0x80;  /* set to IETF variant  */
        return new UUID(randomBytes);
    }

如何使用UUID

Java的JDK1.8是支援UUID生成的,使用下面的語法,即可生成一個版本4的UUID。

System.out.println(UUID.randomUUID().toString());

參考文獻

為什麼不能用uuid做MySQL的主鍵