美文网首页
一文带你了解分布式ID生成器Ray

一文带你了解分布式ID生成器Ray

作者: Java天天 | 来源:发表于2020-05-18 19:34 被阅读0次


    背景

    在应用程序中,经常需要全局唯一的ID作为数据库主键。

    我们需要什么样的ID生成器

    高性能 -> 1.生成性能高 2.插入性能高 3.索引性能高

    高可用 -> 1.依赖中间件要少 2.避免单节点问题

    不重复 -> 1.集群内全局不重复

    易使用 -> 1.接入简单 2.零学习成本

    语义性 -> 1.ID带有一些其它信息

    如何满足这几点要求

    生成性能高 -> 大多数时间为内存分配,减少IO,减少锁

    插入性能高 -> 全局递增,避免页分裂

    索引性能高 -> 数值类型

    全局不重复 -> 分布式一致性

    服务高可用 -> 减少依赖中间件,减少中间件交互时间

    总结特性

    全局递增

    数字类型

    全局唯一

    无锁并发

    内存分配

    单机生成(大多数时间)

    SnowFlake

    Twitter把存储系统从MySQL迁移到Cassandra,因为Cassandra没有顺序ID生成机制,所以开发了这样一套全局唯一ID生成服务。Ray的基本思想来自于SnowFlake,解决了一些SnowFlake中存在的一些问题

    如何保证单机递增不重复

    最简单的思路:时间戳+序列号

    伪代码:

    If 当前时间 > 上次ID生成时间 -> 当前时间+序列号0

    If 当前时间 = 上次ID生成时间 -> 当前时间 + 上次序列号+1

    If 当前时间 < 上次ID生成时间 -> 是否存在这种情况

    如何保证全局不重复

    如何判断两个ID是重复的?我们认为两个ID每一位都是重复的则两个ID重复。

    两个重复的数值如果分别拼接上不同的数值,则最终这两个数值不相同,如:

    1111和1111,分别拼上1和2

    则1111-1和1111-2是不相同的

    之前我们在通过时间戳+序列号实现单机内不重复

    那么我们只需要保证单机内不重复的ID + 不重复的实例ID(workId)就能保证最终生成的ID不重复

    如何保证workId不重复

    这是一个分布式一致性问题

    这个可以使用分布式锁实现每个实例独占一个workId

    并且这仅在启动时和续约时会依赖中间件

    即使依赖的中间件中间暂时不可用,只是新的服务不能使用,旧的正常

    阶段性总结

    全局递增(时间戳自带全局递增)-> 插入,索引高性能

    内存分配(分配时无IO)-> 生成高性能

    单机生成(仅启动时强依赖中间件)-> 高可用

    SnowFlake存在的问题

    时钟偏斜问题

    现代计算机至少有两种不同的时钟:时钟和单调钟。尽管它们都衡量时间,但区分这两者很重要,因为它们有不同的目的。

    时钟

    它根据某个日历返回当前日期和时间。例如: 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生成时间记录在当前实例内存中

    如果时钟回拨发生在重启时,上次ID生成时间记录会丢

    此时新实例拿到了它的workId,并且新机器的时间戳更早就会出现ID重复的情况

    所以需要后台定期同步全局最大的时间戳到中间件,实例退出和启动前同步时间戳

    无尽的序列号

    SnowFlake为什么会出现序列号只有4096位的情况?

    1.位数就那么多,你可以从机器位上分配

    2.没有进位的空间,只支持当前时间戳>=上次时间戳

    Ray支持当前时间<上次时间

    意味着4096位用完可以往时间戳上进位,解决突发流量

    Ray

    Ray参考了SnowFlake的设计,解决了时钟回调和序列号不足的问题,并且提供了更好的并发性能Github地址:github.com/KeshawnVan/…


    上面都是自己整理好的!我就把资料贡献出来给有需要的人!顺便求一波关注。

    学习我们是认真的,拿大厂offer是势在必得的。java(想了解更多点一下哦)

    作者:KeshawnVan

    链接:https://juejin.im/post/5e9d402df265da48094da80d

    相关文章

      网友评论

          本文标题:一文带你了解分布式ID生成器Ray

          本文链接:https://www.haomeiwen.com/subject/ixppohtx.html