java代码获取UUID的实现示例
作者:西晋的no1
什么是UUID
UUID 是指(Universally Unique Identifier)通用唯一识别码,128位。RFC 4122描述了具体的规范实现。
现实问题
我们开发的时候,数据库表总会有一个主键,以前我们可能会使用自增的数字作为主键。这样做去确实查询的时候比较快, 但是在做系统集成或者数据迁移的的时候就麻烦了。这是id就有可能重复了。那么有什么比较好的方法解决这一问题呢? 于是jdk1.5出了UUID这个类来生成唯一的字符串标识。
UUID作用
UUID 的目的是让分布式系统中的所有元素都能有唯一的识别信息。如此一来,每个人都可以创建不与其它人冲突的 UUID,就不需考虑数据库创建时的名称重复问题。其作用视场景而定。
目前最广泛应用的 UUID,即是微软的 Microsoft's Globally Unique Identifiers (GUIDs),而其他重要的应用, 则有 Linux ext2/ext3 档案系统、LUKS 加密分割区、GNOME、KDE、Mac OS X 等等。
UUID定义
UUID使用16进制表示,共有36个字符(32个字母/数字+4个连接符"-")组成,格式为8-4-4-4-12 ;【一个字母/数字只代表4个bit,所以是(8+4+4+4+12)*4=128位;】
由一组32个16进制数码(0-9a-z)所构成,故 UUID 理论上的总数为,约等于
也就是说若每纳秒产生1百万个 UUID,要花100亿年才会将所有 UUID 用完。
格式:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
示例:
96b31816-ae1c-11ed-904f-4531ee40a9e3
格式中M和N都有具体的含义
数字 M的四位表示 UUID 版本,当前规范有5个版本,M可选值为1, 2, 3, 4, 5 ;
数字 N的一至四个最高有效位表示 UUID 变体( variant ),有固定的两位10xx因此只可能取值8, 9, a, b。
这5个版本使用不同算法,利用不同的信息来产生UUID,各版本有各自优势,适用于不同情景。具体使用的信息
- 版本 1:UUID 是根据时间和 MAC 地址生成的;
- 版本 2:UUID 是根据标识符(通常是组或用户 ID)、时间和节点 ID生成的;
- 版本 3:UUID 是通过散列(MD5 作为散列算法)名字空间(namespace)标识符和名称生成的;
- 版本 4:UUID 使用随机性或伪随机性生成;
- 版本 5:类似于版本 3(SHA1 作为散列算法)。
故UUID每个版本不是根据精度区分的,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。
目前大多数使用的 UUID 大都是变体 1,N 的取值是 8、9、a、b 中的一个。
version 1——date-time & MAC address
基于时间戳及MAC地址的UUID实现。它包括了48位的MAC地址和60位的时间戳,
v1为了保证唯一性,当时间精度不够时,会使用13~14位的clock sequence来扩展时间戳,比如:
当UUID的生产成速率太快,超过了系统时间的精度。时间戳的低位部分会每增加一个UUID就+1的操作来模拟更高精度的时间戳,换句话说,就是当系统时间精度无会区分2个UUID的时间先后时,为了保证唯一性,会在其中一个UUID上+1。所以UUID重复的概率几乎为0,时间戳加扩展的clock sequence一共有74bits,(2的74次方,约为1.8后面加22个零),即在每个节点下,每秒可产生1630亿不重复的UUID(因为只精确到了秒,不再是74位,所以换算了一下)。
相对于其它版本,v1还加入48位的MAC地址,这依赖于网卡供应商能提供唯一的MAC地址,同时也可能通过它反查到对应的MAC地址。Melissa病毒就是这样做到的。
Version2(date-time Mac address)
这是最神秘的版本,RFC没有提供具体的实现细节,以至于大部分的UUID库都没有实现它,只在特定的场景(DCE security)才会用到。所以绝大数情况,我们也不会碰到它。
Version3,5(namespace name-based)
V3和V5都是通过hash namespace的标识符和名称生成的。V3使用MD5作为hash函数,V5则使用SHA-1。
因为里面没有不确定的部分,所以当namespace与输入参数确定时,得到的UUID都是确定唯一的。
具体的流程就是
把namespace和输入参数拼接在一起,如"http/http://wwwbaidu.com" ++ "/query=uuid";
使用MD5算法对拼接后的字串进行hash,截断为128位;
把UUID的Version和variant字段都替换成固定的;
如果需要to_string,需要转为16进制和加上连接符"-"。
把V3的hash算法由MD5换成SHA-1就成了V5。
Version4(random)
这个版本使用最为广泛:
其中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)还要大很多倍。
UUID的重复概率
Java中 UUID 使用版本4进行实现,所以由java.util.UUID类产生的 UUID,128个比特中,有122个比特是随机产生,4个比特标识版本被使用,还有2个标识变体被使用。利用生日悖论,可计算出两笔 UUID 拥有相同值的机率约为
其中x为 UUID 的取值范围,n为 UUID 的个数。
以下是以 x =
计算出n笔 UUID 后产生碰撞的机率:
换句话说,每秒产生10亿笔 UUID ,100年后只产生一次重复的机率是50%。如果地球上每个人都各有6亿笔 UUID,发生一次重复的机率是50%。与被陨石击中的机率比较的话,已知一个人每年被陨石击中的机率估计为170亿分之1,也就是说机率大约是0.00000000006 (6 x ),等同于在一年内生产2000亿个 UUID 并发生一次重复。
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,不过需要传递一个名称的字节数组作为参数。
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。
总结
使用较多的是版本1和版本4,其中版本1使用当前时间戳和MAC地址信息。版本4使用(伪)随机数信息,128bit中,除去版本确定的4bit和variant确定的2bit,其它122bit全部由(伪)随机数信息确定。
因为时间戳和随机数的唯一性,版本1和版本4总是生成唯一的标识符。若希望对给定的一个字符串总是能生成相同的 UUID,使用版本3或版本5。如果只是需要生成一个唯一ID,你可以使用V1或V4。
- V1基于时间戳和Mac地址,这些ID有一定的规律(你给出一个,是有可能被猜出来下一个是多少的),而且会暴露你的Mac地址。
- V4是完全随机(伪)的。
如果对于相同的参数需要输出相同的UUID,你可以使用V3或V5。
- V3基于MD5 hash算法,如果需要考虑与其它系统的兼容性的话,就用它,因为它出来得早,大概率大家都是用它的。
- V5基于SHA-1 hash算法,这个是首选。
到此这篇关于java代码获取UUID的实现示例的文章就介绍到这了,更多相关java获取UUID内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!