欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

浅谈常用的分布式ID的设计方案以及Snowfake是否受冬令时切换影响

程序员文章站 2022-03-08 17:53:03
...

浅谈常用的分布式ID的设计方案以及Snowfake是否受冬令时切换影响

分布式ID定义

  1. 全局唯一:区别于单点系统的唯一,全局是要求分布式系统内唯一。
  2. 有序性:通常都要保证生成的ID是有序递增的。例如,在数据库存储场景中,有序ID便于确定数据位置,往往更加高效。

典型实现方案

基于数据库自增序列的实现

这种方式的优缺点都非常明显,好处是简单易用,但是在扩展性和可靠性方面存在局限性。
优点 : 对于数据库自增方案,除了实现简单,它生成的ID还能够保证固定步长的递增,使用很方面。对于单点故障的解决方案是利用Master-slave的主从复制模式。
缺点: 但是,因为每次获取一个ID就会触发数据库的写请求,是一个代价高昂的操作,构建高扩展性、高性能解决方案比较复杂,性能上限明显,更不用谈扩容等场景的难度了,与此同时,保证数据库方案的高可用性也存在挑战,数据库可能发生宕机,即使采取主从热备等各种措施,也可能出现ID重复的现象。

UUID方案

UUID是Universally Unique Identifier的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符。UUID在其他语言中也叫GUID,在java中,生成UUID的代码很简单:

String uuid = UUID.randomUUID().toString();

一个UUID是16字节长的数字,一共128位。通常以36字节的字符串表示,比如:3a5c3b08-5501-4a1b-8b38-446e73582375。 使用的时候,可以把中间的4个中划线去掉,剩下32位字符串:3a5c3b0855014a1b8b38446e73582375。

UUID经由一定的算法机器生成,为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的只能由计算机生成。
优点 : 本地生成ID,不需要进行远程调用,时延低,性能高。
缺点 : UUID过长,16字节128位,通常以36长度的字符串表示,很多场景不适用,比如,由于UUID没有排序,无法保证趋势递增,用做数据库索引字段的效率就很低,太长了,存储空间大,新增记录存储入库时性能差。

Redis方案

Redis 有计数器实现,利用此功能可以实现分布式ID。(increment)
优点 : 性能好,有序,可读性好。
缺点 : 缺点是为了防单点故障,需要引入Redis集群,增加了额外编码和配置的工作量。

Snowflake方案

基于Twitter早期开源的Snowfake的实现,以及相关改动方案。这是目前应用相对比较广泛的一种方式,其结构定义示意图:
浅谈常用的分布式ID的设计方案以及Snowfake是否受冬令时切换影响
整体长度通常是64 (1 + 41 + 10+ 12 = 64)位,适合使用Java语言中的long类型来存储。

符号位说明:第一位为不能为负的符号位:0

时间戳说明:41位记录时间戳timeMillis,当前系统时间,Java 中通常用System.currentTimeMillis()获取long 型数字,转化二进制刚好41位。存在2038年问题

工作机器ID说明
10位记录工作机器id;即datacenterId (5位数据id) + workerId (5位机器id)
datacenterId 与 workerId的最大值十进制值是31(不能为负数)
原因:5位数的最大二进制表示: 0001 1111 —> 十进制:31
***说明:

12位自增***sequence
Sequence的最大十进制值是4095(不能为负数)
原因:12位数的最大二进制表示:1111 1111 1111 —> 十进制:4095

核心算法
计算所需默认常量
默认固定时间twepoch: 1488834975643(毫秒)(小于当前时间即可,不能出现负数)
时间戳偏移位数 timestampLeftShift:22(给5位机器id、5位数据id、12位***移除位置)
数据id偏移位数 datacenterIdShift:17(给5位数据id、12位***移除位置)
机器id偏移位数 workerIdShift:12(给12位***移除位置)
开始计算
一,需要输入的条件(以这4个数据为例):
当前时间 timwstamp:1523244373577(毫秒)
机器id workerId:8(本机ip)
数据id datacentId:12(当前进程Hashmap)
***sequence:123(自增***)
二,计算:
计算公式:

return ((timwstamp-twepoch) << timestampLeftShift) | (datacentId << datacenterIdShift) | (workerId << workerIdShift) | sequence;

优点 : Snowfake方案的好处是算法简单,依赖也非常少,生成的序列可预测,性能也非常好。
缺点 : 时钟偏斜问题(Clock Skew)。我们知道普通的计算机系统时钟并不能保证长久的一致性,可能发生时钟回拨等问题,这就会导致时间戳不准确,进而产生重复ID。从理论上来说,类似Snowfake的方案由于时间数据位数的限制,存在与2038年问题相似的理论极限.

Snowfake是否受冬令时切换影响

我认为没有影响,你可以从Snowfake的具体算法实现寻找答案。我们知道Snowfake算法的Java实现,大都是依赖于System.currentTimeMillis(),这个数值代表什么呢? 从Javadoc可以看出,它是返回当前时间和1970年1月1号UTC时间相差的毫秒数,这个数值与夏/冬令时并没有关系,所以并不受其影响。