天天看點

UUID是否适合做訂單ID問題

目錄

UUID定義(直接查的)

UUID設計目的

UUID的組成

UUID的版本

      version1: 基于時間的

      version2: 基于DCE的

      version3: 基于MD5的

      version4: 基于僞随機數的

      version5: 基于SHA1的

UUID的樣例

UUID是否可以做訂單ID?

UUID為什麼不建議做訂單ID?      

首先-很難區分先後時間:

其次-表空間占用更大:

最後-對于mysql的B+Tree效率更低:

那麼什麼方法更适合生存訂單ID呢?

UUID定義

      UUID是國際标準化組織(ISO)提出的一個概念。UUID是一個128比特的數值,這個數值可以通過一定的算法計算出來。為了提高效率,常用的UUID可縮短至16位。UUID用來識别屬性類型,在所有空間和時間上被視為唯一的辨別。一般來說,可以保證這個值是真正唯一的任何地方産生的任意一個UUID都不會有相同的值。使用UUID的一個好處是可以為新的服務建立新的辨別符。這樣一來,用戶端在查找一個服務時,隻需要在它的服務查找請求中指出與某類服務(或某個特定服務)有關的UUID,如果服務的提供者能将可用的服務與這個UUID相比對,就傳回一個響應。

UUID設計目的

       UUID的設計目的簡單來說,就是讓分布式系統中的所有元素,都能有一個唯一ID。UUID我們一般做開發的都會用到。生成一個UUID也很簡單。

UUID的組成

      三部分組成。

      目前日期和時間,UUID的第一個部分與時間有關,如果你在生成一個UUID之後,過幾秒又生成一個UUID,則第一個部分不同,其餘相同。

      時鐘序列。

      全局唯一的IEEE機器識别号,如果有網卡,從網卡MAC位址獲得,沒有網卡以其他方式獲得      

      時間戳(目前日期+時間)+ 時鐘序列 + 機器識别号(MAC、其他)

UUID的版本

      uuid有五個版本。分别是:

      version1: 基于時間的

          基于時間的UUID通過計算目前時間戳、随機數和機器MAC位址得到。由于在算法中使用了MAC位址,這個版本的UUID可以保證在全球範圍的唯一性。但與此同時,使用MAC位址會帶來安全性問題,這就是這個版本UUID受到批評的地方。如果應用隻是在區域網路中使用,也可以使用退化的算法,以IP位址來代替MAC位址--Java的UUID往往是這樣實作的(當然也考慮了擷取MAC的難度)。

      version2: 基于DCE的

          DCE(Distributed Computing Environment)安全的UUID和基于時間的UUID算法相同,但會把時間戳的前4位置換為POSIX的UID或GID。這個版本的UUID在實際中較少用到

      version3: 基于MD5的

          基于名字的UUID通過計算名字和名字空間的MD5散列值得到。這個版本的UUID保證了:相同名字空間中不同名字生成的UUID的唯一性;不同名字空間中的UUID的唯一性;相同名字空間中相同名字的UUID重複生成是相同的

      version4: 基于僞随機數的

          根據随機數,或者僞随機數生成UUID。這種UUID産生重複的機率是可以計算出來的,但随機的東西就像是買彩票:你指望它發财是不可能的

      version5: 基于SHA1的

          和基于名字的UUID算法類似,隻是散列值計算使用SHA1(Secure Hash Algorithm 1)算法

UUID的樣例

@Test
    public void uuid(){
        String uuid = UUID.randomUUID().toString();
        System.out.println(uuid);
    }
           

3b50fd3e-387e-49e6-844a-2dc407ed2223

be1540de-1265-423a-b5e4-c140da294899

檢視源碼:

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);
    }
           

  我們可以發現,java.util自帶的uuid是采用的版本4。也就基于僞随機數的算法版本。

UUID是否可以做訂單ID?

      uuid在不考慮時鐘回撥問題的基礎上,其實是可以做訂單ID的,但是絕對不能使用java.util的基于僞随機數的方式。而是建議采用版本1,基于時間的。任何基于随機數的方法都不能確定不重複問題

UUID為什麼不建議做訂單ID?      

但是我們為什麼一般不希望采用UUID做訂單呢?

首先-很難區分先後時間:

      uuid很難通過肉眼判斷訂單的先後關系,必須去看建立時間。

其次-表空間占用更大:

      資料庫類型中,我們看一下下表,資料庫的類型大小

MySql常用資料類型

字段類型 大小
tinyint 1位元組
int 4位元組
bigint 8位元組
char 0-255位元組
varchar 0-65535位元組

            如果使用uuid的方式,一般UUID生成以後都是一個36長度字串組成,我們必須采用varchar類型存儲,非常占用空間

最後-對于mysql的B+Tree效率更低:

      資料入庫操作:因為資料庫都有事務性要求,存儲引擎是InnoDB。該引擎存儲資料的時候采用的是B+Tree存儲。B+Tree的資料區是天然有序的。UUID的資料大小不可控的,根據B+Tree的要求,為了保持資料有序,資料等于插入操作。而如果訂單ID是有序的,資料隻需要追加操作就好了。

      資料庫查詢操作:  mysql查詢資料庫的時候,是需要先将資料從磁盤加載到記憶體中。這是一個标準的I/O動作。InnoDb每次加載資料的基本機關是16KB。如果UUID占了36byte,而int是4byte。那麼每次加載資料分别是:16KB/36byte和16KB/4byte。可以看到,UUID因為占用空間更大,IO的讀取次數肯定會相應增加

那麼什麼方法更适合生存訂單ID呢?

雪花算法、美團的leaf算法等

繼續閱讀