UUID在Java中的實現與應用

UUID是什麼

UUID的全稱爲:Universally Unique IDentifier,也被稱爲GUID(Globally Unique IDentifier)。是一種由算法生成的惟一標識,它實質上是一個128位長的二進制整數。一般表示成32個16進制數組成的字符串,如:21EC2020-3AEA-1069-A2DD-08002B30309D。關於UUID標準的rfc定義詳見:http://www.ietf.org/rfc/rfc4122.txt。 固然,GUID一詞有時也專指微軟對UUID標準的實現,用於Windows操做系統中。java

UUID的實現

UUID的格式是這樣的:xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx,一共爲32個16進制數。
M那個位置,表明版本號,目前UUID的標準實現有5個版本,因此只會是1,2,3,4,5
N那個位置,只會是8,9,a,bpython

UUID的具體實現存在多個版本,分別爲:git

1. 基於時間的UUID

基於時間的UUID經過計算當前時間戳、隨機數和機器MAC地址獲得。因爲在算法中使用了MAC地址,這個版本的UUID能夠保證在全球範圍的惟一性。但與此同時,使用MAC地址會帶來安全性問題,這就是這個版本UUID受到批評的地方。若是應用只是在局域網中使用,也可使用退化的算法,以IP地址來代替MAC地址。github

2. DCE(Distributed Computing Environment)安全的UUID

和基於時間的UUID算法相同,但會把時間戳的前4位置換爲POSIX的UID或GID,這個版本的UUID在實際中較少用到。算法

3. 基於名稱空間的UUID(MD5)

基於名稱的UUID經過計算名稱和名稱空間的MD5散列值獲得,這個版本的UUID保證了:相同名稱空間中不一樣名稱生成的UUID的惟一性;不一樣名稱空間中的UUID的惟一性;相同名稱空間中相同名稱的UUID重複生成是相同的。數據庫

4. 基於隨機數的UUID

根據隨機數,或者僞隨機數生成UUID。這種UUID產生重複的機率是能夠計算出來的,但隨機的東西就像是買彩票:你期望它發財是不可能的,但狗屎運一般會在不經意中到來。可能在測試的時候多線程併發也不見得出現重複,可是卻不能保證系統正式上線以後不會出現不重複的UUID,特別是在分佈式系統中。數組

5. 基於名稱空間的UUID(SHA1)

和版本3的UUID算法相似,只是散列值計算使用SHA1(Secure Hash Algorithm 1)算法。安全

在Java中默認實現了基於名稱空間的UUID(UUID Version 3)和基於僞隨機數的UUID(UUID Version 4),分別爲:多線程

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

除了Java默認的實現以外,還有一個開源的UUID實現庫能夠參考:https://github.com/cowtowncoder/java-uuid-generator, 這個庫支持實現UUID的V1,V3,V4和V5版本,在須要使用到惟一性ID的地方能夠酌情考慮使用。併發

關於UUID使用的思考和總結

UUID是爲了解決標識惟一性而提出的,這在分佈式應用場景下很是常見。例如,用戶登陸Token,數據庫記錄主鍵ID等等。可是對因而否可使用UUID(除了考慮惟一性以外,可能還要考慮有序性),以及使用哪一個版本的UUID實現(考慮到效率等因素)須要慎重。例如:雖然UUID能夠解決惟一性,可是卻不適合直接用於數據庫記錄主鍵ID,對於數據庫主鍵ID而言,除了考慮惟一性以外,還要考慮有序性,索引效率等因素。而在用戶登陸Token標識這種場景下使用UUID是能夠的,甚至在使用手機或郵箱做爲惟一名稱標識的場景下,可使用基於名稱空間的UUID。
一般來說,若是僅僅須要實現惟一性需求,那麼對於使用UUID有以下建議:

  • 對於暴露MAC地址不敏感的場合,使用UUID V1是最佳選擇。固然了,也能夠經過對UUID進行MD5散列的方式進行保密,不過這須要考慮性能開銷。
  • 若是能夠保證在指定命名空間內的名稱惟一性,例如手機號或者郵箱,那麼選擇UUID V3或者V5的實現也能保證惟一性。
  • 對於UUID V4,若是是基於僞隨機數的實現,是存在出現重複UUID的機率的,若是對於ID惟一性要求不是十分嚴格的場景,這個版本的實現也能夠考慮。

另外,在各個語言平臺對應UUID實現的支持各不相同。
1.Java語言
默認只支持V3和V4(基於僞隨機數)兩種版本的實現

2.Python語言
支持V1,V3,V4,V5版本的UUID實現
Python的UUID V1基於時間戳和MAC地址,最後12個16進制字符就是網卡地址。

>>> import uuid
>>> uuid.uuid1()
UUID('d3a173de-0ca9-11e8-af24-f0d5bf9aedc1')
>>> uuid.uuid1()
UUID('73e4ac9e-0caa-11e8-aa82-f0d5bf9aedc1')

Python支持UUID V3實現,對名稱空間內的字符串進行MD5散列值生成UUID。

>>> uuid.uuid3(uuid.NAMESPACE_DNS,"chench")
UUID('a0fda26d-acf1-37da-ad64-7cac7753de92')
>>> uuid.uuid3(uuid.NAMESPACE_DNS,"chench")
UUID('a0fda26d-acf1-37da-ad64-7cac7753de92')

Python的UUID V4實現基於僞隨機數實現,這種UUID產生重複的機率是能夠計算出來的。

>>> uuid.uuid4()
UUID('d1437e20-95eb-446a-b9ca-9184013b8542')
>>> uuid.uuid4()
UUID('4dce6000-0ad5-4f35-84d8-6b434205d212')

與UUID V3的算法一致,不一樣的是UUID V5的散列算法爲SHA1。

>>> uuid.uuid5(uuid.NAMESPACE_DNS,"chench")
UUID('b8643db9-49d8-5a98-842d-14f3215f08cb')
>>> uuid.uuid5(uuid.NAMESPACE_DNS,"chench")
UUID('b8643db9-49d8-5a98-842d-14f3215f08cb')

【參考】
https://zh.wikipedia.org/wiki/通用惟一識別碼 UUID
https://zh.wikipedia.org/wiki/全局惟一標識符 GUID
https://www.jianshu.com/p/d77f3ef0868a 關於UUID的二三事
https://qtdebug.com/java-duplicate-uuid/ 測試 Java 生成 UUID 是否重複
http://www.infoq.com/cn/articles/talk-about-the-history-of-uuid UUID簡史

相關文章
相關標籤/搜索