雪花算法和UUID
UUID
UUID是一种唯一且不需要中央协调的ID,它使用某种规则创建ID,而不是某种中心化的自增方式,使得其成为创建成本最低的ID类型。
到目前为止UUID一共有5个实现版本
-
版本1: 按照 UUID 定义的每个字段的意义来实现,使用的变量因子是时间戳+时钟序列+节点信息(Mac地址),考的最多的版本
-
版本2:基本和1一致,信息很少,可忽略
-
版本3:基本name和namespace的hash实现变量因子,使用MD5进行hash,不是随机生成
-
版本4:使用随机或者伪随机实现变量因子,不具备单调性,使用最广泛
-
版本5:基本name和namespace的hash实现变量因子,使用sha1进行hash,不是随机生成
这些版本的结构基本都是一致的,只是在版本1的基础上变化了变量因子
UUID长度128比特,换算为16进制数就是有32个16进制数组成,中间用4个-分割,所以一共有36个字符,按照8-4-4-4-4-12的的顺序进行分割。
组成它的变量因子一共有三个,Timestamp时间戳、Clock Sequence时钟序列,node节点信息。
Timestamp时间戳:60bit 15个数
这15个数分布在前3个组成中,分别是时间戳低位8个、时间戳中位4个、时间戳高位3个,第三个部分的第一位是UUID版本字段,占4个字节,所以可以有16个版本号
Clock Sequence时钟序列:14bit 三个半数,最左边的2bit是保留位
时钟序列一般用于时间调整时的唯一性保证,当时间调整,或者 nodeId 变化的时候,直接使用一个随机数,或者,在原先的Clock Sequence值上面自增加一也是可以的。
保留位用于未来的扩展以及保持UUID的标准兼容性,确保UUID的格式能够适应将来的需求而不影响现有的实现。
Node节点信息:12个数,一般是MAC地址或者随机数生成
基于name和namespace的hash结构生成是什么意思?
- Name: 是对象的名称或标识符。比如,在 Kubernetes 中,Pod 的名称就是一个典型的
name
。 - Namespace: 是一个命名空间,用于将不同作用域内的对象区分开来。例如,两个相同名称的 Pod 可以存在于不同的命名空间,从而不冲突。
在 UUID v3 和 UUID v5 中,name
和 namespace
是生成 UUID 的关键输入,它们并不是自动变化的,而是由开发者根据具体应用场景指定的。每次生成 UUID 时,使用相同的 name
和 namespace
组合,会得到相同的 UUID。
雪花算法
雪花算法生成的64位ID由以下几部分组成:
第一位(符号位):由于ID都是正整数,所以第一位始终为0。
时间戳(41位):记录时间戳的差值(相对于某个固定时间点),单位是毫秒。41位时间戳可以使用69年(从1970年开始,可用至2039年)。
工作机器id(10位):用于标识不同的工作机器(如不同的服务器实例),支持在同一数据中心内部署最多1024台机器。
如果存在跨机房部署的情况下可以把这10个bit位,拆分成两个5bit,前5个bit表示机房id,后面5个表示机器的id
序列号(12位):用于在同一毫秒内产生不同的ID,支持每个工作机器在同一毫秒内产生最多4096个ID。
时钟回拨和时钟漂移
时钟回拨是指系统时间被人为或自动地调整到过去的某个时间点。
时钟漂移是指由于硬件本身的物理特性,系统时钟的计时速率可能并不完全准确,随着时间推移,会逐渐偏离标准时间。
雪花算法怎么解决时钟回拨和时钟漂移?
-
时间强依赖变成弱依赖,只有第一次算法启动的时候获取时间戳,之后都是算法增加时间戳,而不是依赖于系统时间。这种方式可以避免一毫秒只能生成4096个ID的弊端。仍然会有ID冲突,例如,一小时内获取一千万条ID,这个实际上是获取了未来的ID,这样如果服务器重启,获取了旧的时间戳,就会出现ID重复,解决方案是存储当前使用到的时间戳,下一次启动时获取。
-
通过机器ID或者序列号进行补偿,可以增加机器ID的部分或者增加序列号的部分,UUID也是这种方案。
-
阻塞等待时间追赶(兜底策略)。