越过时针回拨的坑, 解决重启导致的雪花算法id重复问题, 文末附送踩坑总结大礼包!

Posted 李昊轩的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了越过时针回拨的坑, 解决重启导致的雪花算法id重复问题, 文末附送踩坑总结大礼包!相关的知识,希望对你有一定的参考价值。

在上一篇文章中
通过PowerMockito来模拟时钟回拨, 验证改进版雪花算法是否起效
我们了解了什么是时针回拨, 怎样去解决它, 但是像雪花算法这种高性能的id生成器, 比较娇贵, 比如在我将这个算法投入到生产之前, 发现这个算法在重启之后再压测的时候, 会出现大量重复的id, 当时就猜测可能算法本身有一个记载状态的属性, 去读源码, 果然找到了, 这个属性叫epoch.

Id重复发生原因:

epoch对应该算法第一次初始化传入的时间, 也就是算法的生日~~

因为这个epoch在每次启动的时候默认传入的都是变化的, 例如当前毫秒数, 导致了当这个算法所在的宿主项目重启的时候, 这个epoch都会变化, 出现了新epoch毫秒数>老epoch毫秒数的情况

Id是用下面公式计算, 会用这个epoch时间和currentTimeMills来进行左移或运算, 算出最终Id.

Number number = currentMillis - EPOCH << 22 | workerId << 12 | this.sequence;

直接导致的结果就是, 老id比新id要大, 类似一个大号的时针回拨, 时间可以减小Id生成的系数, 上面图示的差值同样可以, Id就是这样重复的.

解决方案:

将Id设置为定值, 例如2021年1月1号的毫秒数, 这里还有一个坑, 就是有的日期类(calendar类说的就是你)获取指定日期的毫秒数是变化的, 这里推荐LocalDateTime来生成. 同时注意时区问题.

static {
        long fixedEpoch = LocalDateTime.of(2021, 1, 1, 0, 0, 0, 0).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli();
        // EPOCH是服务器第⼀次上线时间点, 设置后不允许修改
        EPOCH = fixedEpoch;
    }

分享经验:

这个算法确实需要耐心, 坑比较多, 而且例如在分布式的环境下, 如果没有唯一的雪花Id生成服务, 采用分布式多点同时并发生成Id的话, 算法的workerId需要分布式锁来避免初始化重复, epochtime需要设置为统一的时间防止出现epoch重置, 并且时针回拨触发的备用workerId必须不能和任意分布式节点的workerId相同.

以上是关于越过时针回拨的坑, 解决重启导致的雪花算法id重复问题, 文末附送踩坑总结大礼包!的主要内容,如果未能解决你的问题,请参考以下文章

雪花算法(SnowFlake)

雪花算法snowflake分布式id生成原理详解,以及对解决时钟回拨问题几种方案讨论

雪花算法snowflake分布式id生成原理详解,以及对解决时钟回拨问题几种方案讨论

修改redis zset的score为64位整形以支持雪花ID算法

10分钟拿下雪花算法

ID号生成 雪花算法