什么是 UUID ?通用唯一识别码的简单介绍

摘要:UUID 是通用唯一识别码(Universally Unique Identifier)的缩写,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的,是由一组 32 位的十六进制数字组成,表现出来的形式。

简介

UUID 是通用唯一识别码(Universally Unique Identifier)的缩写,是一种由算法生成的二进制长度为 128 位的数字标识符,即由 128 比特组成。

比特(BIT,Binary digit),计算机专业术语,是信息量的最小单位,由英文 BIT 音译而来。同时也是二进制数字中的位,二进制数的一位所包含的信息就是一比特,如二进制数 0100 就是 4 比特。

UUID 根据标准方法生成,不依赖中央机构的注册和分配,具有唯一性,这与其它大多数编号方案不同。重复 UUID 码概率接近零,可以忽略不计。

其目的是让分布式系统中的所有元素,都能有唯一的辨识信息。如此一来,每个人都可以创建不与其它人冲突的 UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。

目前最广泛应用的 UUID,是微软公司的全局唯一标识符(GUID),而其他重要的应用,则有 Linux ext2/ext3 文件系统、LUKS 加密分区、GNOME、KDE、Mac OS X 等等。

GUID 全局唯一标识符(Globally Unique Identifier)是微软对 UUID 标准的实现,UUID 还有其它各种实现,不止 GUID 一种。

什么是 UUID ?通用唯一识别码的简单介绍

组成

UUID 是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成的 API,按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片 ID 码和随机数。

UUID 有以下几部分的组合:

  • UUID 的第一个部分与时间有关,如果你在生成一个 UUID 之后,过几秒又生成一个 UUID,则第一个部分不同,其余相同;
  • 时钟序列;
  • 全局唯一的 IEEE 机器识别号,如果有网卡,从网卡 MAC 地址获得,没有网卡以其他方式获得;
  • 在 hibernate(Java orm 框架)中, 采用 IP - JVM 启动时间 - 当前时间右移 32 位 - 当前时间 - 内部计数 来组成 UUID(8-8-4-8-4)。
IEEE,电气与电子工程师协会(Institute of Electrical and Electronics Engineers),总部位于美国纽约,是一个国际性的电子技术与信息科学工程师的协会,也是全球最大的非营利性专业技术学会。IEEE 大部分成员是电子工程师、计算机工程师和计算机科学家。

MAC 地址

网络中每台设备都有一个唯一的网络标识,这个地址叫 MAC 地址(局域网地址、以太网地址或网卡地址),由网络设备制造商生产时,写在网卡的 EPROM(一种闪存芯片,通常可以通过程序擦写)。

MAC 地址则是 48 位的(6 个字节),通常表示为 12 个十六进制数,每 2 个十六进制数之间用冒号隔开,如 08:00:20:0A:8C:6D 就是一个 MAC 地址。

具体如下图所示,其前 3 字节表示 OUI 组织唯一标识符,是 IEEE 的注册管理机构给不同厂家分配的代码,区分不同的厂家,后 3 字节由厂家自行分配。

什么是 UUID ?通用唯一识别码的简单介绍

格式

UUID 是一种由算法生成的二进制长度为 128 位(即由 128 比特组成)的数字标识符,编码采用十六进制,所以它表现出来的形式,是由一组 32 位的十六进制数字,显示在由 4 个连字符分隔 '-' 的五个组中。

// 格式:8-4-4-4-12
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

UUID 理论上的总数为 1632=2128,约等于 3.4 x 10123。也就是说若每纳秒产生一百万个 UUID,要花 100 亿年才会将所有 UUID 用完。

其中 M 与 N 都有特殊含义:

  • 数字 M 表示 UUID 版本,当前规范有 5 个版本,M 可选值为 1,2,3,4,5;
  • 数字 N 表示 UUID 变体,目前只会出现变体 1 的 8,9,a,b 四种情况。

变体(Variants)

为了能兼容过去的 UUID,以及应对未来的变化,因此有了变体(Variants)这一概念。目前已知的变体有如下几种:

  • variant 0:0xxx。为了向后兼容预留;
  • variant 1:10xx。当前正在使用的;
  • variant 2:110x。为早期微软 GUID 预留;
  • variant 3:111x。为将来扩展预留,目前暂未使用。

因此,可以认为,目前正在使用的 UUID 都是 variant 1,取值是 8,9,a,b 中的一个。

版本(Version)

对于“变体 1”和“变体 2”,标准中定义了 5 个版本,这 5 个版本使用不同算法,利用不同的信息来产生 UUID,各版本有各自优势,适用于不同情景。

Nil UUID

Nil UUID是一个特例,值为 00000000-0000-0000-0000-000000000000 ;也就是说,所有位都设置为 0。

版本 1(时间 + MAC 地址)

版本 1 的 UUID 是基于当前时间戳、随机数和节点 ID(硬件标识,通常为无线网卡的 MAC 地址)等数据计算生成的,其中:

  • 1~8 位采用系统时间,在系统时间上精确到毫秒级保证时间上的唯一性;
  • 9~16 位采用底层的 IP 地址,在服务器集群中的唯一性;
  • 17~24 位采用当前对象的 HashCode 值,在一个内部对象上的唯一性;
  • 25~32 位采用调用方法的一个随机数,在一个对象内的毫秒级的唯一性。

优点:由于在算法中使用了 MAC 地址,这个版本的 UUID 可以保证在全球范围的唯一性。

缺点:使用 MAC 地址会带来安全性问题,这就是这个版本 UUID 受到批评的地方。

如果应用只是在局域网中使用,也可以使用退化的算法,以 IP 地址来代替 MAC 地址(Java 的 UUID 往往是这样实现的,当然也考虑了获取 MAC 的难度)。

版本 2(DCE 安全版本)

DCE(Distributed Computing Environment,分布式计算环境)安全版本的 UUID 根据标识符(通常是组或用户 ID)、时间和节点 ID 生成。

和版本 1 的 UUID 算法相同,但会把时间戳的前 4 位置换为 POSIX 的 UID 或 GID,这个版本的 UUID 在实际中较少用到。

版本 3(MD5 散列)

用户指定一个名字空间(namespace)和一个字符串,通过 MD5 散列生成 UUID。这个版本的 UUID 保证了:

  • 相同名字空间中不同名字生成的 UUID 的唯一性;
  • 不同名字空间中的 UUID 的唯一性;
  • 相同名字空间中相同名字的 UUID 重复生成是相同的。

MD5 算法能够生成 128 bit 的数据,然后将其中的 4 bit 改为固定的版本号,1-3 bit 改为变体。

版本 4(随机 UUID)

根据随机数(或伪随机数)生成 UUID,其中 4 bit 为固定的版本号,1-3 bit 改为变体,其它为随机生成。

理论上各个设备生成的随机数,重复的可能性很低。但是由于一些伪随机数发生器缺少必要的熵,来产生足够的伪随机数。如果不能很好的保证随机性,建议采用版本 3 或版本 5 生成 UUID。

版本 5(SHA1 散列)

与版本 3 类似,用户指定一个名字空间和一个字符串,通过 SHA1 散列生成 UUID(其中使用的字符串必须唯一)。

只是 SHA1 生成 160 bit 的散列,需要截断为 128 bit。

总结

  • 使用较多的是版本 1 和 4,因为时间戳和随机数的唯一性,总是生成唯一的标识符,适合应用于分布式计算环境,具有高度的唯一性;
  • 版本 3 和 5适合于一定范围内名字唯一,且需要或可能会重复生成 UUID 的环境;
  • 版本 2 在实际中较少用到。

通常我们建议使用 UUID 来标识对象或持久化数据,但以下情况最好不使用 UUID:

  • 映射类型的对象,比如只有代码及名称的代码表;
  • 人工维护的非系统生成对象,比如系统中的部分基础数据。

为了提高效率,常用的 UUID 可缩短至 16 位。

碰撞

重复机率

当多次生成相同的 UUID 并将其分配给不同的指示对象时,就会发生冲突。对于使用来自网卡的唯一 MAC 地址的标准版本 1 和 2 的 UUID ,只有当实施与标准不同时,无论是无意还是故意,都可能发生冲突。

与版本 1 和 2 相比,UUID 使用随机生成的节点 ID,基于散列的版本 3 和版本 5 的 UUID ,以及随机版本 4 的 UUID ,即使没有实现问题也可能发生冲突,尽管可能性很小,通常可能是忽略。

可以利用生日悖论,来精确地计算该概率。

例如,至少碰撞一次具有 50% 的概率,根据版本 4 需要生成的 UUID 的数量是 2.71x1018,计算如下:

什么是 UUID ?通用唯一识别码的简单介绍

这个数字相当于大约 85 年每秒产生 10 亿个 UUID ,每个 UUID 16 bytes/字节,包含这么多 UUID 的文件,大约 45 艾字节(EB),比目前存在的最大数据库大很多倍,它们都在数百 PB 的数量级。

最小数必须查找碰撞要的概率来生成版本 4 的 UUID 的 p 由下式近似计算:

什么是 UUID ?通用唯一识别码的简单介绍

因此,在 103 万亿 个 版本 4 UUID 中找到重复的概率是十亿分之一,产生重复 GUID 并造成错误的情况非常低,是故大可不必考虑此问题。

机率也与随机数产生器的质量有关,若要避免重复机率变高,必须要使用基于密码学上的强伪随机数产生器来生成值才行。

生日悖论

生日悖论是指,一个房间要多少人,两个人的生日相同的概率会大于50%? 答案是23人。这就意味着在一个典型的标准小学班级(30人)中,存在两人生日相同的概率为 70%。对于 60 或者更多的人,这种概率会大于 99%。

从引起逻辑矛盾的角度来说,生日悖论是一种“佯谬”(看上去是一个错误,但实际上不是)。这个数学事实十分反直觉,故称之为一个悖论。

简而言之,大多数人之所以会认为 23 人中有 2 人生日相同的概率应该远远小于 50%,是因为将问题理解为“其他 22 人与我的生日相同的概率”,而非问题本意“23 人之中两两之间存在生日相同”。如果考虑到这一点,直觉上会将原来的概率乘以23(注:此算法并不正确),则会意识到概率很大了。

数据库主键

我们开发的时候,数据库表总会有一个主键,以前我们可能会使用自增 int 型作为主键,这样做确实查询的时候比较快。

但是在做系统集成或者数据迁移的的时候就麻烦了,这时 ID 就有可能重复了,于是 jdk1.5 出了 UUID 这个类来生成唯一的字符串标识。

那 UUID 用作数据库表的主键,和自增 int 型作为主键的比较,有哪些优势和劣势?

优势

  • UUID 在不同的表、数据库、甚至服务器中都是全局唯一的,所以你可以合并来自不同数据库,甚至是不同服务器上不同数据库上的数据行;
  • UUID 不会在 URL 中暴露你的数据信息;
  • 例如,一个客户可以通过 id10 来访问他的账号地址 http://www.dedenotes.com/c/10/ ,他可以很轻松地猜到会有 id 11、12 等等的客户,这可能被拖库,或被别人猜到你的用户量。
  • UUID 生成的时候不需要查一遍数据库,并且它还简化了应用层的逻辑。
  • 例如,当你要给父表和子表插入数据时,一般你要先把数据插到父表里,然后才能插到子表里。但是如果你用 UUID 的话,你可以直接生成父表的主键,然后在一个事务里同时把数据插到父表和子表里。

劣势

  • 占用空间大。存储 UUID(16 字节)需要的占用空间,比 int 型(4 字节)甚至 BIGint 型(8 字节)都要大;
  • 调试困难。你可以想象一下平时你只需要 WHERE id = 10 现在你要写 WHERE id = 'df3b7cb7-6a95-11e7-8846-b05adad3f0ae';
  • 无序。UUID 通常会因为它未被排序的问题导致数据库页分裂,查询及索引效率低;
  • 可读性差。

其实 UUID 和自增 int 型各有各的优点与缺点,一般来讲自增 int 型更适合用于单机系统,性能方面确实要优于 UUID。

但是在分布式、微服务、多系统集成的时候,可以使用 UUID,不是说 UUID 在分布式架构的效率表现更高,而是说可以避免自增 int 型作为主键的一些缺陷,保证全局 ID 唯一性。

版权声明:本文为博主原创文章,未经博主允许不得转载。http://www.dedenotes.com/html/UUID.html
(1)
打赏 微信扫一扫 微信 支付宝 QQ 扫码打赏

meta

Dedenotes 赞(3)

meta 是 html 语言 head 区的一个辅助性标签,位于文档的头部,不包含任何内容,标签的属性定义了与文档相关联的名称/值对。meta 标签可提供相关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词。

HTTP消息结构 HTTP请求报文和响应报文的格式

Dedenotes 赞(3)

HTTP 协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的一种网络传输协议,基于 TCP/IP 通信协议来传递数据(HTML 文件, 图片文件, 查询结果等),所有的 WWW(World Wide Web)文件都必须遵守这个标准。

防止表单重复提交的 4 种方法

Dedenotes 赞(3)

平时开发的项目中可能会出现下面这些情况:由于用户误操作,多次点击表单提交按钮;由于网速等原因造成页面卡顿,用户重复刷新提交页面;黑客或恶意用户使用 Postman 等工具重复恶意提交表单(攻击网站)。