UUID和雪花算法Snowflake

481 阅读6分钟

UUID

概述

UUID (Universally Unique Identifier),通用唯一识别码。UUID是基于当前时间、计数器(counter)和硬件标识(通常为无线网卡的MAC地址)等数据计算生成。

格式

UUID 由以下几部分的组合:

  1. 当前日期和时间,UUID 的第一个部分与时间有关,如果你在生成一个 UUID 之后,过几秒又生成一个 UUID,则第一个部分不同,其余相同。
  2. 时钟序列。
  3. 全局唯一的 IEEE 机器识别号,如果有网卡,从网卡 MAC 地址获得,没有网卡以其他方式获得。

UUID 是由一组32位数的16进制数字(128位二进制比特位)所构成,以连字号分隔的五组来显示,形式为8-4-4-4-12,总共有36个字符(即三十二个英数字母和四个连字号)。例如:

c814423c-a26f-853f-a0bc-91cd92a31d3b
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

数字M的表示 UUID 版本,当前规范有5个版本,M(十六进制)可选值为1, 2, 3, 4, 5 ;

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

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

  • version 1, date-time & MAC address
    基于时间的UUID通过计算当前时间戳、随机数和节点标识:机器MAC地址得到。由于在算法中使用了MAC地址,这个版本的UUID可以保证在全球范围的唯一性。但与此同时,使用MAC地址会带来安全性问题,这就是这个版本UUID受到批评的地方。同时, Version 1没考虑过一台机器上起了两个进程这类的问题,也没考虑相同时间戳的并发问题,所以严格的Version1没人实现,Version1的变种有Hibernate的CustomVersionOneStrategy.java、MongoDB的ObjectId.java、Twitter的snowflake等。

  • version 2, date-time & group/user id
    DCE(Distributed Computing Environment)安全的UUID和基于时间的UUID算法相同,但会把时间戳的前4位置换为POSIX的UID或GID。这个版本的UUID在实际中较少用到。

  • version 3, MD5 hash & namespace
    基于名字的UUID通过计算名字和名字空间的MD5散列值得到。这个版本的UUID保证了:相同名字空间中不同名字生成的UUID的唯一性;不同名字空间中的UUID的唯一性;相同名字空间中相同名字的UUID重复生成是相同的。

  • version 4, pseudo-random number
    根据随机数,或者伪随机数生成UUID。

  • version 5, SHA-1 hash & namespace
    和版本3的UUID算法类似,只是散列值计算使用SHA1(Secure Hash Algorithm 1)算法。

使用较多的是版本1和版本4,其中版本1使用当前时间戳和MAC地址信息。版本4使用(伪)随机数信息,128bit中,除去版本确定的4bit和variant确定的2bit,其它122bit全部由(伪)随机数信息确定。若希望对给定的一个字符串总是能生成相同的 UUID,使用版本3或版本5。

UUID 是由一组32位数的16进制数字所构成,是故 UUID 理论上的总数为163216^{32}=21282^{128},约等于3.4×101233.4\times10^{123}。也就是说若每纳秒产生1百万个 UUID,要花100亿年才会将所有 UUID 用完。

优缺点

优点
  • 简单,代码方便。
  • 生成ID性能非常好,基本不会有性能问题。本地生成,没有网络消耗。
  • 全球唯一,在遇见数据迁移,系统数据合并,或者数据库变更等情况下,可以从容应对。
缺点
  • 采用无意义字符串,没有排序,无法保证趋势递增。
  • UUID使用字符串形式存储,数据量大时查询效率比较低
  • 存储空间比较大,如果是海量数据库,就需要考虑存储量的问题。

雪花算法 Snowflake

概述

SnowFlake 算法,是 Twitter 开源的分布式 id 生成算法。其核心思想就是:使用一个 64 bit 的 long 型的数字作为全局唯一 id。在分布式系统中的应用十分广泛,且 id 引入了时间戳,基本上保持自增的。其原始版本是 scala 版,后面出现了许多其他语言的版本如 Java、C++ 等。

格式

image.png

  • 1bit - 首位无效符
  • 41bit - 时间戳(毫秒级)
    41位可以表示 24112^{41}-1 个数字;
    24112^{41}-1 毫秒,换算成年就是表示 69 年的时间
  • 10bit - 工作机器id
    5bit - datacenterId机房id
    5bit - workerId机器id
  • 12bit - 序列号 序列号,用来记录同一个datacenterId中某一个机器上同毫秒内产生的不同id。

特点(自增、有序、适合分布式场景)

  • 时间位:可以根据时间进行排序,有助于提高查询速度。
  • 机器id位:适用于分布式环境下对多节点的各个节点进行标识,可以具体根据节点数和部署情况设计划分机器位10位长度,如划分5位表示进程位等。
  • 序列号位:是一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号

snowflake算法可以根据项目情况以及自身需要进行一定的修改

优缺点

优点

  • 毫秒数在高位,自增序列在低位,整个ID都是趋势递增的。
  • 不依赖数据库等第三方系统,以服务的方式部署,稳定性更高,生成ID的性能也是非常高的。
  • 可以根据自身业务特性分配bit位,非常灵活。

缺点

  • 雪花算法在单机系统上ID是递增的,但是在分布式系统多节点的情况下,所有节点的时钟并不能保证不完全同步,所以有可能会出现不是全局递增的情况。如果系统时间被回调,或者改变,可能会造成id冲突或者重复。

参考文章

  1. 分布式id生成(UUID、雪花算法snowflake