在应用程序中,常常须要全局惟一的ID做为数据库主键。git
Twitter把存储系统从MySQL迁移到Cassandra,由于Cassandra没有顺序ID生成机制,因此开发了这样一套全局惟一ID生成服务。Ray的基本思想来自于SnowFlake,解决了一些SnowFlake中存在的一些问题github
最简单的思路:时间戳+序列号 伪代码:数据库
If 当前时间 > 上次ID生成时间 -> 当前时间+序列号0
If 当前时间 = 上次ID生成时间 -> 当前时间 + 上次序列号+1
If 当前时间 < 上次ID生成时间 -> 是否存在这种状况
复制代码
如何判断两个ID是重复的?咱们认为两个ID每一位都是重复的则两个ID重复。bash
两个重复的数值若是分别拼接上不一样的数值,则最终这两个数值不相同,如:并发
1111和1111,分别拼上1和2分布式
则1111-1和1111-2是不相同的性能
以前咱们在经过时间戳+序列号实现单机内不重复学习
那么咱们只须要保证单机内不重复的ID + 不重复的实例ID(workId)就能保证最终生成的ID不重复spa
这是一个分布式一致性问题设计
这个可使用分布式锁实现每一个实例独占一个workId
而且这仅在启动时和续约时会依赖中间件
即便依赖的中间件中间暂时不可用,只是新的服务不能使用,旧的正常
现代计算机至少有两种不一样的时钟:时钟和单调钟。尽管它们都衡量时间,但区分这二者很重要,由于它们有不一样的目的。
它根据某个日历返回当前日期和时间。例如: Java中的System.currentTimeMillis()返回自epoch(1970年1月1日 午夜 UTC,格里高利历)以来的秒数(或毫秒),根据公历日历,不包括闰秒。
适用于测量持续时间(时间间隔),例如Java中的System.nanoTime()都是单调时钟。这个名字来源于他们保证老是前进的事实。
时钟的问题在于,虽然它们看起来简单易用,但却具备使人惊讶的缺陷:一天可能不会有精确的86,400秒,时钟可能会先后跳跃,而一个节点上的时间可能与另外一个节点上的时间彻底不一样。 若是时间往前拨咱们就没法确保时间戳+序列号生成的ID是单机惟一的。
SnowFlake单位毫秒内生成的ID数不能超过12位的序列位
也就是说SnowFlake严重依赖于当前时间戳,而且只能处理当前时间戳大于等于上次时间戳的状况,对于当前时间戳小于上次时间戳的状况没法处理。
If 当前时间 > 上次ID生成时间 -> 当前时间+序列号0
If 当前时间 = 上次ID生成时间 -> 当前时间 + 上次序列号+1
If 当前时间 < 上次ID生成时间 ->上次ID生成时间 + 上次序列号 +1
复制代码
时钟偏斜,时间前跳其实就是等价于当前时间 < 上次ID生成时间
首先上次ID生成时间记录在当前实例内存中 若是时钟回拨发生在重启时,上次ID生成时间记录会丢 此时新实例拿到了它的workId,而且新机器的时间戳更早就会出现ID重复的状况 因此须要后台按期同步全局最大的时间戳到中间件,实例退出和启动前同步时间戳
SnowFlake为何会出现序列号只有4096位的状况? 1.位数就那么多,你能够从机器位上分配 2.没有进位的空间,只支持当前时间戳>=上次时间戳 Ray支持当前时间<上次时间 意味着4096位用完能够往时间戳上进位,解决突发流量
Ray参考了SnowFlake的设计,解决了时钟回调和序列号不足的问题,而且提供了更好的并发性能 Github地址:github.com/KeshawnVan/…