对于分布式,我们说下其中的分布式ID的定义
要求往往是
全局唯一,区别于单点系统的唯一,要求分布式系统内唯一
有序,保证生成的ID是有序递增的,在数据库存储中,有序性可以方便确定数据位置,更加高效
常见的有
基于数据库自增序列的实现,优缺点都很明显,但是特别简单
或者基于Twitter的Snowflake实现,这是常见的方式,结构定义如下
一般长度为64位,适合使用Java的long来存储
头部是表示正负的
然后是41位的时间戳,使用System.currentTimeMillis()
后面是10位的WorkerID,标准定义为5位数据中心 + 5位机器ID
最后12位就是单位毫秒生成的序列号的理论极限
其余的唯一ID解决方案,很多可以算是Snowflake的变种
诸如Reids Zookeeper MonogoDB等中间件等,MonogoDB的ObjectId提供了一个12byte的ID定义,32位用于记录秒为单位的事件,机器ID是24位,16位进程ID,24位随机技术序列
Snowflake是否受着冬令时的影响
可以从Snowflake的具体算法实现认为,因为时间是依赖于System.currentTimeMillis()的,这个时间是返回的1970年1月1号UTC时间相差的毫秒数,和冬令时没有关系
对于我们业务需要的分布式ID,除了唯一和有序,需要考虑哪些要素?
我们首先要理解下,需要一个什么样的分布式ID
除了唯一和有序之外,考虑到分布式系统的功能,我们需要分布式ID
有意义,能够包含更多业务上的信息
高可用,分布式系统的必然要求
紧凑型,ID的大小受到应用的制约,太长的ID会降低MySQL等数据库索引的性能
对于主流的方案
首先是数据库自增方案,除了实现起来比较简单,生成的Id还能保证步长的递增,使用起来方便,但是每一次获取一个ID,就要触发数据库的一次写请求,代价高昂,性能上限明显
一般大厂有着自己的复合架构,美团的架构Leaf-Segment,有着缓存作用的Leaf层,对数据库操作通过数据库中间件提供的批量操作,能保证性能和高可用
snowflake的方案则是算法简单,依赖比较少,生成序列可预测,性能很好
但是其有着一定的不足,比如时钟偏斜问题,普通的计算机系统不能保证长久的一致性,可能发生时钟回拨的问题,导致时间戳不准确
所以Twitter建议开启NTP,为了保证时间一致的问题,不然可能导致时间戳不准确,产生重复ID
对于这个问题,可以缓存上一个时间戳,来判断是否时间不合理
但是序列号的可以预测是把双刃剑,虽然简化了部分工程,但是如果是安全相关的,可能被黑客利用
ID设计阶段需要谨慎的考虑暴露的信息,Erlang的flake基于了Mac地址计算,所以在安全敏感领域不可取
而且存在这时间数据位数的问题,可能和2038年相似极限问题,迟早有一日要面对
更加深入的对时钟和分布式时序的问题,比如不同机器的事件是不一致的,对于如何保证事件的有序性,我们可以看Time, Clocks, and the Ording of Events in a Distributed System