java代碼獲取UUID的實現(xiàn)示例
什么是UUID
UUID 是指(Universally Unique Identifier)通用唯一識別碼,128位。RFC 4122描述了具體的規(guī)范實現(xiàn)。
現(xiàn)實問題
我們開發(fā)的時候,數(shù)據(jù)庫表總會有一個主鍵,以前我們可能會使用自增的數(shù)字作為主鍵。這樣做去確實查詢的時候比較快, 但是在做系統(tǒng)集成或者數(shù)據(jù)遷移的的時候就麻煩了。這是id就有可能重復了。那么有什么比較好的方法解決這一問題呢? 于是jdk1.5出了UUID這個類來生成唯一的字符串標識。
UUID作用
UUID 的目的是讓分布式系統(tǒng)中的所有元素都能有唯一的識別信息。如此一來,每個人都可以創(chuàng)建不與其它人沖突的 UUID,就不需考慮數(shù)據(jù)庫創(chuàng)建時的名稱重復問題。其作用視場景而定。
目前最廣泛應用的 UUID,即是微軟的 Microsoft's Globally Unique Identifiers (GUIDs),而其他重要的應用, 則有 Linux ext2/ext3 檔案系統(tǒng)、LUKS 加密分割區(qū)、GNOME、KDE、Mac OS X 等等。
UUID定義
UUID使用16進制表示,共有36個字符(32個字母/數(shù)字+4個連接符"-")組成,格式為8-4-4-4-12 ;【一個字母/數(shù)字只代表4個bit,所以是(8+4+4+4+12)*4=128位;】
由一組32個16進制數(shù)碼(0-9a-z)所構(gòu)成,故 UUID 理論上的總數(shù)為,約等于
也就是說若每納秒產(chǎn)生1百萬個 UUID,要花100億年才會將所有 UUID 用完。
格式:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
示例:
96b31816-ae1c-11ed-904f-4531ee40a9e3
格式中M和N都有具體的含義
數(shù)字 M的四位表示 UUID 版本,當前規(guī)范有5個版本,M可選值為1, 2, 3, 4, 5 ;
數(shù)字 N的一至四個最高有效位表示 UUID 變體( variant ),有固定的兩位10xx因此只可能取值8, 9, a, b。
這5個版本使用不同算法,利用不同的信息來產(chǎn)生UUID,各版本有各自優(yōu)勢,適用于不同情景。具體使用的信息
- 版本 1:UUID 是根據(jù)時間和 MAC 地址生成的;
- 版本 2:UUID 是根據(jù)標識符(通常是組或用戶 ID)、時間和節(jié)點 ID生成的;
- 版本 3:UUID 是通過散列(MD5 作為散列算法)名字空間(namespace)標識符和名稱生成的;
- 版本 4:UUID 使用隨機性或偽隨機性生成;
- 版本 5:類似于版本 3(SHA1 作為散列算法)。
故UUID每個版本不是根據(jù)精度區(qū)分的,Version5并不會比Version1精度高,在精度上,大家都能保證唯一性,重復的概率近乎于0。
為了能兼容過去的 UUID,以及應對未來的變化,因此有了變體(Variants)這一概念。
目前已知的變體有下面 4 種:
- 變體 0:格式為 0xxx,為了向后兼容預留。
- 變體 1:格式為 10xx,當前正在使用的。
- 變體 2:格式為 11xx,為早期微軟的 GUID 預留。
- 變體 3:格式為 111x,為將來的擴展預留,目前暫未使用。
在上例中,M 是 1,N 是 a(二進制為 1010,符合 10xx 的格式),這就意味著這個 UUID 是“版本 1”、“變體 1”的 UUID。
目前大多數(shù)使用的 UUID 大都是變體 1,N 的取值是 8、9、a、b 中的一個。
version 1——date-time & MAC address
基于時間戳及MAC地址的UUID實現(xiàn)。它包括了48位的MAC地址和60位的時間戳,
v1為了保證唯一性,當時間精度不夠時,會使用13~14位的clock sequence來擴展時間戳,比如:
當UUID的生產(chǎn)成速率太快,超過了系統(tǒng)時間的精度。時間戳的低位部分會每增加一個UUID就+1的操作來模擬更高精度的時間戳,換句話說,就是當系統(tǒng)時間精度無會區(qū)分2個UUID的時間先后時,為了保證唯一性,會在其中一個UUID上+1。所以UUID重復的概率幾乎為0,時間戳加擴展的clock sequence一共有74bits,(2的74次方,約為1.8后面加22個零),即在每個節(jié)點下,每秒可產(chǎn)生1630億不重復的UUID(因為只精確到了秒,不再是74位,所以換算了一下)。
相對于其它版本,v1還加入48位的MAC地址,這依賴于網(wǎng)卡供應商能提供唯一的MAC地址,同時也可能通過它反查到對應的MAC地址。Melissa病毒就是這樣做到的。
Version2(date-time Mac address)
這是最神秘的版本,RFC沒有提供具體的實現(xiàn)細節(jié),以至于大部分的UUID庫都沒有實現(xiàn)它,只在特定的場景(DCE security)才會用到。所以絕大數(shù)情況,我們也不會碰到它。
Version3,5(namespace name-based)
V3和V5都是通過hash namespace的標識符和名稱生成的。V3使用MD5作為hash函數(shù),V5則使用SHA-1。
因為里面沒有不確定的部分,所以當namespace與輸入?yún)?shù)確定時,得到的UUID都是確定唯一的。
具體的流程就是
把namespace和輸入?yún)?shù)拼接在一起,如"http/http://wwwbaidu.com" ++ "/query=uuid";
使用MD5算法對拼接后的字串進行hash,截斷為128位;
把UUID的Version和variant字段都替換成固定的;
如果需要to_string,需要轉(zhuǎn)為16進制和加上連接符"-"。
把V3的hash算法由MD5換成SHA-1就成了V5。
Version4(random)
這個版本使用最為廣泛:
其中4位代表版本,2-3位代表variant。余下的122-121位都是全部隨機的。即有2的122次方(5.3后面36個0)個UUID。一個標準實現(xiàn)的UUID庫在生成了2.71萬億個UUID會產(chǎn)生重復UUID的可能性也只有50%的概率:
這相當于每秒產(chǎn)生10億的UUID,持續(xù)85年,而把這些UUID都存入文件,每個UUID占16bytes,總需要45 EB(exabytes),比目前最大的數(shù)據(jù)庫(PB)還要大很多倍。
UUID的重復概率
Java中 UUID 使用版本4進行實現(xiàn),所以由java.util.UUID類產(chǎn)生的 UUID,128個比特中,有122個比特是隨機產(chǎn)生,4個比特標識版本被使用,還有2個標識變體被使用。利用生日悖論,可計算出兩筆 UUID 擁有相同值的機率約為
其中x為 UUID 的取值范圍,n為 UUID 的個數(shù)。
以下是以 x =
計算出n筆 UUID 后產(chǎn)生碰撞的機率:
換句話說,每秒產(chǎn)生10億筆 UUID ,100年后只產(chǎn)生一次重復的機率是50%。如果地球上每個人都各有6億筆 UUID,發(fā)生一次重復的機率是50%。與被隕石擊中的機率比較的話,已知一個人每年被隕石擊中的機率估計為170億分之1,也就是說機率大約是0.00000000006 (6 x ),等同于在一年內(nèi)生產(chǎn)2000億個 UUID 并發(fā)生一次重復。
Java獲取uuid示例
使用 JDK 原生的API
import java.util.UUID; public class Test { public static void main(String[] args) { // JDK 原生的 API 獲取UUID // uuid版本3獲取uuid UUID uuid3 = UUID.nameUUIDFromBytes("test".getBytes()); int version3 = uuid3.version(); System.out.println("UUID3:" + uuid3 + " 版本 " + version3); // uuid版本4獲取uuid UUID uuid4 = UUID.randomUUID(); int version4 = uuid4.version(); System.out.println("UUID4:" + uuid4 + " 版本 " + version4); // 生成一個基于指定 UUID 字符串的 UUID 對象 UUID uuid = UUID.fromString("098f6bcd-4621-3373-8ade-4e832627b4f6"); int version = uuid.version(); System.out.println("UUID_fromString:" + uuid + " 版本 " + version); } }
nameUUIDFromBytes() 會生成一個版本 3 的UUID,不過需要傳遞一個名稱的字節(jié)數(shù)組作為參數(shù)。
randomUUID() 方法生成了一個版本 4 的 UUID,這也是生成 UUID最方便的方法。如果只使用原生 JDK 的話,基本上都用的這種方式。
fromString() 方法會生成一個基于指定 UUID 字符串的 UUID對象,如果指定的 UUID 字符串不符合 UUID 的格式,將拋出 IllegalArgumentException 異常。
使用com.fasterxml.uuid.Generators
除了使用 JDK 原生的 API 之外,還可以使用com.fasterxml.uuid.Generators,需要先在項目中加入該類的 Maven 依賴。
<dependencies> <dependency> <groupId>com.fasterxml.uuid</groupId> <artifactId>java-uuid-generator</artifactId> <version>3.1.4</version> </dependency> </dependencies>
代碼:
import com.fasterxml.uuid.Generators; import java.util.UUID; public class Test { public static void main(String[] args) { // JDK 原生的 API 獲取UUID // uuid版本1獲取uuid UUID uuid1 = Generators.timeBasedGenerator().generate(); System.out.println("UUID : " + uuid1); System.out.println("UUID 版本 : " + uuid1.version()); // uuid版本4獲取uuid UUID uuid2 = Generators.randomBasedGenerator().generate(); System.out.println("UUID : " + uuid2); System.out.println("UUID 版本 : " + uuid2.version()); } }
Generators.timeBasedGenerator().generate() 可用于生成版本1 的 UUID,Generators.randomBasedGenerator().generate() 可用于生成版本4 的 UUID。
總結(jié)
使用較多的是版本1和版本4,其中版本1使用當前時間戳和MAC地址信息。版本4使用(偽)隨機數(shù)信息,128bit中,除去版本確定的4bit和variant確定的2bit,其它122bit全部由(偽)隨機數(shù)信息確定。
因為時間戳和隨機數(shù)的唯一性,版本1和版本4總是生成唯一的標識符。若希望對給定的一個字符串總是能生成相同的 UUID,使用版本3或版本5。如果只是需要生成一個唯一ID,你可以使用V1或V4。
- V1基于時間戳和Mac地址,這些ID有一定的規(guī)律(你給出一個,是有可能被猜出來下一個是多少的),而且會暴露你的Mac地址。
- V4是完全隨機(偽)的。
如果對于相同的參數(shù)需要輸出相同的UUID,你可以使用V3或V5。
- V3基于MD5 hash算法,如果需要考慮與其它系統(tǒng)的兼容性的話,就用它,因為它出來得早,大概率大家都是用它的。
- V5基于SHA-1 hash算法,這個是首選。
到此這篇關(guān)于java代碼獲取UUID的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)java獲取UUID內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?SE使用for?each循環(huán)遍歷數(shù)組的方法代碼
在Java?SE開發(fā)中,數(shù)組是最常見的數(shù)據(jù)結(jié)構(gòu)之一,Java提供了多種遍歷數(shù)組的方式,其中for循環(huán)是最常用的方式之一,本文將介紹如何使用for?each循環(huán)遍歷數(shù)組,接下來,我們將通過一個簡單的代碼示例來展示如何使用for?each循環(huán)遍歷數(shù)組,需要的朋友可以參考下2023-11-11SpringBoot基于數(shù)據(jù)庫的定時任務統(tǒng)一管理的實現(xiàn)
這篇文章主要介紹了SpringBoot基于數(shù)據(jù)庫的定時任務統(tǒng)一管理的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12