让我寝食难安的“雪花算法”

先科普:

雪花算法是一种由Twitter(现在是X)推广的分布式全局唯一ID的算法。关于雪花这个名称的解释,我自以为是因为雪花是随机飘落然后越堆越高的,前天同事问我雪花的由来是不是因为每片雪花都不一样,我觉得这个解释更浪漫。

回归主题,简单说这是一种生成不重复ID的算法,有别于UUID, 雪花具备自增性的,方便数据库存储和使用(可排序,体积也不大)

原理是一个63位(二进制)的正整数Long值,前41位保存毫秒级时间戳,中间10位存放机器唯一标识,剩下12位是用于1毫秒内序列递增, 如果1毫秒内递增序号值满12位,那就等待直至时间毫秒数+1再继续。

我手上有老一个项目,使用的是一套自编的ID算法:

  • ID也是纯数字
  • 使用数据库保存最新id
  • 应用启动时从数据库获取5000个id备用 (then update id+=5000 to DB), 不够再拿。

这套算法有一个问题, 就是每个环境id进度不一样, 导致dev的配置数据无法直接迁移到sit,同理sit无法迁移到uat,导致配置成本很大。 所以我提议并主导引入雪花算法,经过一轮技术预研+组会讨论影响+1周dev+1月sit….. 雪花算法被推送到了生产。

然!

生产环境竟然出现ID冲突,初期以为因为机器码冲突

ps:dev发现Long值太长导致前端模块出现了异常,所以雪花缩水成53位,主要是机器码由10位缩成4位,序号从12位缩成8位,机器码基于hutool实现(使用ip)

然后就是夜里挑灯看…bug。经过了两日两夜的emjo:

先试调整算法,机器码增到到8,序号缩到4,问题没解决(其实这时候有种不好的预感,8位机器码即可容纳256个pod 竟然这么容易冲突?)

通宵添加了一个唯一机器码计算程序(给serviceName增加一个数字,每次应用上线时从服务注册中心下载清单校验唯一性),问题没解决

第二天白天研读代码发现Snowflake类存在多例情况(Snowflake创建在一个数据逻辑层底层对象类, 基于spring框架先入为主以为是单例,检查发现竟然不是,导致Snowflake类实际被创建多次),逐把Snowflake类属性增加static,信心满满以为解决,发版测试发现还是没解决

到了这个时候, 从代码已经检查不出问题了,后面模拟线上环境编写单元测试没问题, 通过日志埋点(仍怀疑workerId冲突),数据分析等等等一系列调试, 都没有发现问题。

直至现在

嗯, 现在也还没解决, 目前是代码逻辑增加一个外部引入的boolean做算法路由,先暂时切回到了传统算法, 这个五一假期继续研究吧。


回来补一下后续

后面再过滤几轮压测, 都没有再复现了, 有几个可能性:

  1. 当时有其他开发人员连接上了SIT环境, 由于每套环境有独立的注册中心, 所以我做的基于注册中心实现的唯一workerId验证并不可靠(其实前面有考虑过用数据库实现, 但是成本太高且手上还忙着其他事情,就搁置了。
  2. 压测并不到位(其实问题还在), 除非我能亲眼看到问题产生的过程且能够按照缺陷原理复现,否则这种靠外部测试来论证问题结果的有概率性的办法永远让我不得心安。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注