目錄
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很難通過肉眼判斷訂單的先後關系,必須去看建立時間。
其次-表空間占用更大:
資料庫類型中,我們看一下下表,資料庫的類型大小
字段類型 | 大小 |
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算法等