雪花算法详解

政采云技术团队.png

无涯.png 雪花算法是 Twitter 内部使用的分布式全局唯一 ID 生成算法,并将其开源。本文介绍算法原理、及其一些实际使用中的问题和解法。

算法原理

雪花算法生成的是一个 Long 类型的 ID,Long 占用 64 个 bit 位,该算法将 bit 位分为以下部分:

符号位 时间戳 机器 id sequence
占用 1 bit 41 bit 10 bit 12 bit

符号位:通常为 0,代表正数

时间戳:毫秒数,不是直接从 1970 年开始的时间戳,而是代表可使用时间(当前时间减去开始使用时间),大约可使用 2^41 毫秒约 69 年。

机器 id:高 5 位为数据中心 id,低 5 位为机器 id

sequence:在同一毫秒内从 0 开始的计数器,最大 2^12 为 4096 个,当超过 4096 时,则阻塞等待下一毫秒。既每秒单机可支持并发约为 400 万。

算法实现相对简单,本文就不贴代码了。重点是 需要保障机器 id 唯一,可通过配置、数据库、redis、zk 等实现

问题与解法

原始的算法在实际使用场景中,可能会碰到以下问题:

时钟回拨

时钟回拨会导致生成重复的 ID,可通过以下方式解决:

  1. 与最后生成时间做对比
  2. 回拨时间较短时,可阻塞等待
  3. 否则,可以重新获取新的机器 id

此方案可能会导致机器 id 消耗太快,可对时间戳、sequence 占用 bit 进行调整。不过不必太过担心,机器 id 也是可以重复使用的,只要不是使用中的重复就好。

前端 JS 精度

JS 内置有 32 位整数,而 number 类型的安全整数是 53 位。如果超过 53 位,则精度会丢失。当碰见雪花算法时,为了正确表示,则只能使用 2^31 毫秒约 24 天。

解决方案:调整时间戳为秒:则能使用 2^31 秒约 68 年。此时,单机并发则为 4096。如果不够,可以适当调小机器 id bit 位数,调大 sequence 位数,使并发增加。

分库分表

分库分表通常采用取模算法,当路由字段使用了雪花算法时,会有什么问题呢?

数据分布不均匀

在同一时间内,sequence 自增永远从 0 开始。当分库分表采用取模算法时,会导致生成的 ID 一直落在前面的库、表里,造成数据倾斜,影响性能。此种现象在单位时间(毫秒、秒)并发低、或分表较多的场景下尤为突出。

解决方案:sequence 从一个随机数开始。

随机范围选择,需要在以下几点取一个平衡:

  1. 随机范围太大:将会造成 sequence 浪费,并发降低

可通过调大 sequence 占用 bit 解决

  1. 太小:将会导致后续分库分表扩容后,数据又倾斜了

建议:取一个未来可能的最大分表数。

控制路由目标表

当分库分表数据已经发生倾斜不均匀了,该如何解决呢?

假设共有 4 个实例、分库为 16、分表数为 128,数据倾斜将造成第一个实例数据过多、性能降低。需要将后续数据落在其它实例上既不落在 0-31 表上,以降低第一个实例压力,待数据分布均匀后,再使用前面的算法

基于 sequence 随机开始,有以下方案:

方案一

将 sequence 随机开始,递增后的值,执行一下路由算法,当其落在其它实例上时,才返回;否则继续递增。

优点:简单 缺点:id 生成器通常为中间件团队的服务,并且后续需要恢复之前的算法,修改麻烦

方案二

我们知道新的 sequence 要求:

  1. 与老 sequence 一一映射不会重复
  2. 与老 sequence 一样,单调递增
  3. 路由后落在指定的表里

为了讲解方便,我们假设分了 8 张表:0-7。需要使新的 sequence 落在 表 3-6 上。假设 x 轴为从 ID 服务器得到原始 sequence,则 y 可能的值分布如图所示:x、y 都从 0 开始

image-20220529201359797

可以找到规律:y 为从0开始,第 x+1 个有效的值。

当路由运算后落在表3-6上,即为有效

在实现时,注意以下几点:

  1. 正确解析出原始 sequence,并用新 sequence 组装新的 ID
  2. 新 sequence 的最大值,不能大于其占用 bit 所位能代表的最大值

当超过时,可等待后取下一个时间的 sequence

  1. 由于新老 sequence 是一一映射,所以可以提前缓存结果,避免 cpu 消耗

以上两种方案都有一个影响:会浪费更多的 sequence,使最大并发降低,使用时请结合实际情况。

总结

本文介绍了雪花算法原理及其在使用过程中可能出现的问题和解法。如果你在使用中有碰到其它问题或者有更好的解法,请评论留言吧!

推荐阅读

基于流量域的数据全链路治理

一次接口响应时间过长的性能分析及排查过程

MySQL 之 innodb 自增锁原理实现

基于父子关系的高效去重算法

招贤纳士

政采云技术团队(Zero),一个富有激情、创造力和执行力的团队,Base 在风景如画的杭州。团队现有300多名研发小伙伴,既有来自阿里、华为、网易的“老”兵,也有来自浙大、中科大、杭电等校的新人。团队在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊……如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 [email protected]

微信公众号

文章同步发布,政采云技术团队公众号,欢迎关注

政采云技术团队.png

猜你喜欢

转载自juejin.im/post/7114083293577871396
今日推荐