snowflake的一些总结

前言

假如面试时候,问到分布式唯一ID生成的相关问题,我觉得snowflake应绕不开,既然绕不开,那怎么回答呢?

简洁介绍

26093552_8xRi.jpg

64位的二进制表示,分了4块

第1块不用管,0是固定的,表示正数。

核心就在后3块。

第2块:

当前时间戳 减 开始时间戳,单位毫秒

开始时间一般指定位项目上线日期就行。

很多博客都简略的说“时间戳”,包括图片中也是。

很容易混淆,这里存的可不是你用xx语言的一个time相关的API调用得出的那个值。

第3块:

10位,范围 0~1023 = 1024

工作机器ID,就是机器的唯一标志,或者具体点说实例的唯一标志,随便取,不超出位数范围,互相不重复就行。

第4块:

自增序列号,0~4095 = 4096

这3部分的分配只是官方建议的,我们可以根据实际需要自行分配,但是各部分的作用是不变的。

时钟回退

笔者耗费了一天时间,并未找到确切解决时钟回退的方案。

网上一些标题党,不是复制粘贴,就是方案存在漏洞或限制。笔者决定放弃。

笔者认为时钟回退可以分为2种情况

个别机器的微小误差

比如运维写个脚本统一纠正所有机器的时钟,就有那么一些机器,差个三两秒,甚至几毫秒。

其实如果机器真的就差那么点时间,不纠正也可以,那么既然纠正了,其实也是可以有方案应对的。

1、等一等

当判断出现时钟回退情况,可以进行一个短暂的等待,几毫秒后再判断,还是不行,就抛异常。

这种方案,只能解决小部分的微小误差,作用有限。

2、吃回头草

以snowflake为例,同一毫秒同一机器,最多能get出4096个ID,可是你们真的有那么大的并发量么?

换句话说,过去的那些时间,都百分百用完了4096个序列号了么?

那么此时,我们可以记录最近的一段时间的毫秒数和使用了的最大序列号 map<时间戳,max序列号>

此时,当发生时钟回退的时候,我们可以利用之前的时间剩余的那些序列号,保证请求不报错。

这个方案的问题就是,1、分布式ID的时间不准确 2、对于回退较长时间,map还是不够用 3、

3、去隔壁借碗饭

(这个方案是笔者自己想到的,不清楚为什么网上都没人提过,也可能是我没看到)

(大学旁边的小饭馆一条街,经常出现,你去一家店里点饭菜,可是这家生意太好了,米饭卖光了,

这时候饭店老板娘通常会去隔壁饭店买米饭。总不能因为米饭卖没了,这单生意就不做了吧)

那么依此,为什么我们的思维要局限在一个机器上呢?一个机器发生时钟回退了,那就去其他机器上“借一个ID”先用着呗!

总不可能整个集群,所有机器全部都时钟退回了吧,那无解了。

集体回退 / 回退过长

你别管是怎么原因,集群中所有机器,集体时钟回退,或者某一些机器,回退时长过长。

这种情况真的不知道怎么办了,也许直接抛异常,报警是最直接的。

也许解决时钟回拨的问题就是不用时钟

《UidGenerator:百度开源的分布式ID服务(解决了时钟回拨问题)》

这篇文章标题,去搜搜,很多博客都是原模原样的照搬照抄,标题看起来挺唬人的。

其实按照他的说法,也可以说是解决了时钟回拨问题,根本原因是没用时钟进行运算。

没用时钟就不存在时钟回拨问题喽!!!

snowflake的4块中的第二块,说到底就是个数,即使你把它定义为时间戳,也是用2个时间戳相减的差值,

之所以用时间戳,就是因为这样生产的ID是有意义的。

而CachedUidGenerator显然是抛弃了这点,果然,“既要...又要...”、“两手抓两手都要硬”这些都是梦。

笔者简述一下这个过程,并不是CachedUidGenerator的实现,只是笔者对于这个方案的描述和理解。

(实操起来考虑点非常多,至少CachedUidGenerator中用到的RingBuffer,笔者并不理解,哎,留下了没有技术的泪水)

过程是这样的:

比如我们设置基础时间,一般是这个分布式ID生成器项目上线日期,比如 2020-10-10 00:00:00

而第2块的数值 = now() - ‘2020-10-10 00:00:00’ = 差值

现在 now()会受到 时钟回拨的影响,那我们就不计算了,我们自己生成这个‘差值’ 

比如第一个‘差值’是0,第4块有n个序号,那我就基于这个‘差值’预先生成n个ID,缓存起来,get请求进来,直接从缓存中拿,

拿的差不多了(比如缓存里还剩50%了),‘差值’+1 = 1,又能生成n个序号,给缓存满上,再拿一些,再满上,以此类推。

这种分布式ID看起来一点问题没有,非常的snowflake,也不受时钟回拨的影响,唯一的问题就是它的时间是“假的”,并不是ID生成的真正时间。

 

这个方案顺带着解决了另一个问题(算是笔者的理解)

 

你看啊,以snowflake为例,理论上,同一毫秒,第4块能用4096个数,可是你们实际生产生活中,能用上这么些么?

对于一些没什么并发的业务来说,所拿到的ID末尾大部分都是0,就是说大部分是 "0xxxxyyy0"这种情况,

因为没啥毫秒级的并发,每个时间点去拿,都从0开始,造成了ID大部分都是偶数,对于分库分表来说,可能会受一些影响

而用上述方案,就不会,都是从 0~4095排满的,非常的“雨露均沾”。

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值