什么是UUID

 
背景:
 
先说下,为啥要写UUID,有两点吧,一个是我们系统里确实用到了UUID,只知道这个系统自带的方法能生成一个分布式全局唯一的ID,用于数据的唯一性标识;
然后就是今天偶尔看到了一篇文章,说的是如何在分布式系统中实现全局唯一,其中有两个方法,一个简单的是UUID,不过这个不是系统推荐的方法,因为有些缺点,下面会介绍;
还有一个推荐的算法叫 snowflake; 这个我会单起一篇文件详细点介绍;
 
定义:
 
UUID 是指Universally Unique Identifier,翻译为中文是通用唯一识别码,UUID 的目的是让分布式系统中的所有元素都能有唯一的识别信息。如此一来,每个人都可以创建不与其它人冲突的 UUID,就不需考虑数据库创建时的名称重复问题。

UUID 是由一组32位数的16进制数字所构成(注定了存储空间大),是故 UUID 理论上的总数为1632=2128,约等于3.4 x 10123

也就是说若每纳秒产生1百万个 UUID,要花100亿年才会将所有 UUID 用完;

为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的复杂特性在保证了其唯一性的同时,意味着只能由计算机生成。
 


格式:

UUID 的十六个八位字节被表示为 32个十六进制数字,以连字号分隔的五组来显示,形式为 8-4-4-4-12,总共有 36个字符(即三十二个英数字母和四个连字号)。例如:

123e4567-e89b-12d3-a456-426655440000 (其中的字母是16进制表示,大小写无关。)

xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

数字 M的四位表示 UUID 版本,当前规范有5个版本,M可选值为1, 2, 3, 4, 5

数字 N的一至四个最高有效位表示 UUID 变体( variant ),有固定的两位10xx因此只可能取值8, 9, a, b

UUID版本通过M表示,当前规范有5个版本,M可选值为1, 2, 3, 4, 5。这5个版本使用不同算法,利用不同的信息来产生UUID,各版本有各自优势,适用于不同情景。具体使用的信息

  • version 1, date-time & MAC address
  • version 2, date-time & group/user id
  • version 3, MD5 hash & namespace
  • version 4, pseudo-random number
  • version 5, SHA-1 hash & namespace

使用较多的是版本1和版本4,其中版本1使用当前时间戳和MAC地址信息。版本4使用(伪)随机数信息,128bit中,除去版本确定的4bit和variant确定的2bit,其它122bit全部由(伪)随机数信息确定。

因为时间戳和随机数的唯一性,版本1和版本4总是生成唯一的标识符。若希望对给定的一个字符串总是能生成相同的 UUID,使用版本3或版本5。

 

UUID 有着正儿八经的RFC规范,是一个128bit的数字,也可以表现为32个16进制的字符,中间用”-”分割。

- 时间戳+UUID版本号,分三段占16个字符(60bit+4bit),
- Clock Sequence号与保留字段,占4个字符(13bit+3bit),
- 节点标识占12个字符(48bit),

GUID(Globally Unique Identifier)是UUID的别名;但在实际应用中,GUID通常是指微软实现的UUID。


 

UUID Version 1:基于时间的UUID

因为时间戳有60bit,所以可以尽情花;节点标识也有48bit,一般用MAC地址表达,如果有多块网卡就随便用一块。如果没网卡,就用随机数凑数,或者拿一堆尽量多的其他的信息,比如主机名什么的,拼在一起再hash一把。

顺序号这16bit则仅用于避免前面的节点标示改变(如网卡改了),时钟系统出问题(如重启后时钟快了慢了),让它随机一下避免重复。

但好像Version 1就没考虑过一台机器上起了两个进程这类的问题,也没考虑相同时间戳的并发问题,所以严格的Version1没人实现,接着往下看各个变种吧。

 


 

Version1变种 – Hibernate

 

Hibernate的算法,解决了之前version 1的两个问题

- 时间戳(6bytes, 48bit):毫秒级别的,从1970年算起,能撑8925年….
- 顺序号(2bytes, 16bit, 最大值65535): 没有时间戳过了一秒要归零的事,各搞各的,short溢出到了负数就归0。
- 机器标识(4bytes 32bit): 拿localHost的IP地址,IPV4呢正好4个byte,但如果是IPV6要16个bytes,就只拿前4个byte。
- 进程标识(4bytes 32bit): 用当前时间戳右移8位再取整数应付,不信两条线程会同时启动。

值得留意就是,机器进程和进程标识组成的64bit Long几乎不变,只变动另一个Long就够了,这里的long指的就是时间戳+顺序号的8个字节(64位系统的long占8个字节,32位的还是4个字节)。

 


 

现在总结下优缺点:

常见的方式。可以利用数据库也可以利用程序生成,一般来说全球唯一。

优点:

1)简单,代码方便。

2)生成ID性能非常好,基本不会有性能问题。

3)全球唯一,在遇见数据迁移,系统数据合并,或者数据库变更等情况下,可以从容应对。

缺点:

1)没有排序,无法保证趋势递增。

2)UUID往往是使用字符串存储,查询的效率比较低。

3)存储空间比较大,如果是海量数据库,就需要考虑存储量的问题。

4)传输数据量大

5)不可读。

 


 

参考链接:

https://www.jianshu.com/p/da6dae36c290 

https://blog.csdn.net/nawenqiang/article/details/82684001 

posted on 2019-11-04 12:27  古木小永  阅读(2686)  评论(0编辑  收藏  举报

导航