常用的分布式ID的设计方案?Snowfake是否受冬令时切换影响?
严格来说,分布式并不算是Java领域,而是一个单独的大主题,但确实也会在Java技术岗位面试中被涉及。在准备面试时,如果有丰富的分布式系统经验当然好;如果没有,你可以选择典型问题和基础技术进行适当准备。关于分布式,我自身的实战经验也非常有限,专栏里就谈谈从理论出发的一些思考。
谈谈常用的分布式ID的设计方案?Snowfake是否受冬令时切换影响?
典型回答
-
首先,我们需要明确通常的分布式ID定义,基本的要求包括:
- 全局唯一,区别于单点系统的唯一,全局是要求分布式系统内唯一。
- 有序性,通常都需要保证生成的ID是有序递增的。例如,在数据库存储等场景中,有序ID便于确定数据位置,往往更加高效。
-
目前业界的方案很多,典型方案包括:
- 基于数据库自增序列的实现。这种方式优缺点都非常明显,好处是简单易用,但是在扩展性和可靠性等方面存在局限性。
- 基于Twitter早期开源的Snowfake的实现,以及相关改动方案。这是目前应用相对比较广泛的一种方式,其结构定义你可以参考下面的示意图。
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XvmEFba5-1651878368618)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\1651795975749.png)]
- 整体长度通常是64 (1 + 41 + 10+ 12 = 64)位,适合使用Java语言中的long类型来存储。
- 头部是1位的正负标识位。
- 紧跟着的高位部分包含41位时间戳,通常使用System.currentTimeMillis()。
- 后面是10位的WorkerID,标准定义是5位数据中心 + 5位机器ID,组成了机器编号,以区分不同的集群节点。
- 最后的12位就是单位毫秒内可生成的序列号数目的理论极限。
-
Snowfake的官方版本是基于Scala语言,Java等其他语言的参考实现有很多,是一种非常简单实用的方式,具体位数的定义是可以根据分布式系统的真实场景进行修改的,并不一定要严格按照示意图中的设计。
-
Redis、Zookeeper、MangoDB等中间件,也都有各种唯一ID解决方案。其中一些设计也可以算作是Snowfake方案的变种。例如,MongoDB的ObjectId提供了一个12 byte(96位)的ID定义,其中32位用于记录以秒为单位的时间,机器ID则为24位,16位用作进程ID,24位随机起始的计数序列。
-
国内的一些大厂开源了其自身的部分分布式ID实现,InfoQ就曾经介绍过微信的seqsvr,它采取了相对复杂的两层架构,并根据社交应用的数据特点进行了针对性设计,具体请参考相关代码实现。另外,百度、美团等也都有开源或者分享了不同的分布式ID实现,都可以进行参考。
关于第二个问题,Snowfake是否受冬令时切换影响?
- 我认为没有影响,你可以从Snowfake的具体算法实现寻找答案。我们知道Snowfake算法的Java实现,大都是依赖System.currentTimeMillis(),这个数值代表什么呢?
- 从Javadoc可以看出,它是返回当前时间和1970年1月1号UTC时间相差的毫秒数,这个数值与夏/冬令时并没有关系,所以并不受其影响。
考点分析
- 今天的问题不仅源自面试的热门考点,并且也存在着广泛的应用场景,我前面给出的回答只是一个比较精简的典型方案介绍。我建议你针对特定的方案进行深入分析,以保证在面试官可能会深入追问时能有充分准备;如果恰好在现有系统使用分布式ID,理解其设计细节是很有必要的