Mybatis-plus雪花算法机器码冲突解决

问题及原因分析

发现线上异常报错,定位日志提示 主键插入冲突,以往并未发现出现过类似问题,查询日志是从 2022-12-16 后pod扩容后有偶发性产生。猜测原因为雪花算法生成的 ID 有重复,项目中雪花算法使用的是mybatis-plus中提供的工具类com.baomidou.mybatisplus.core.toolkit.Sequence生成,怀疑dataCenterId 和workerId有重复

雪花算法简介:


Snowflake,雪花算法是由Twitter开源的分布式ID生成算法,以划分命名空间的方式将 64-bit位分割成多个部分,每个部分代表不同的含义。而 Java中64bit的整数是Long类型,所以在 Java 中 SnowFlake 算法生成的 ID 就是 long 来存储的。

​ ○ 第1位占用1bit,其值始终是0,可看做是符号位不使用。

​ ○ 第2位开始的41位是时间戳,41-bit位可表示2^41个数,每个数代表毫秒,那么雪花算法可用的时间年限是(1L<<41)/(1000L360024*365)=69 年的时间。

​ ○ 中间的10-bit位可表示机器数,即2^10 = 1024台机器,但是一般情况下我们不会部署这么台机器。如果我们对IDC(互联网数据中心)有需求,还可以将 10-bit 分 5-bit 给 IDC,分5-bit给工作机器。这样就可以表示32个IDC,每个IDC下可以有32台机器,具体的划分可以根据自身需求定义。

​ ○ 最后12-bit位是自增序列,可表示2^12 = 4096个数。

这样的划分之后相当于在一毫秒一个数据中心的一台机器上可产生4096个有序的不重复的ID。但是我们 IDC 和机器数肯定不止一个,所以毫秒内能生成的有序ID数是翻倍的。

目前使用 Mybatis-plus 提供的雪花算法工具类 com.baomidou.mybatisplus.core.toolkit.Sequence。默认实现中间10位机器码,分为前5位 dataCenterId 和 后5位 workerId

dataCenterId默认生成策略:

以虚拟机mac地址后两位进行数据摘要运算,对 32 取余

workerId 默认生成策略:

取的JVM进程 pid 与 dataCenterId运算 ,线上pid默认都是1

由以上默认生成机器码的算法,目前线上有两台机器的dataCenterId和workerId重复,就会导致在高并发下生成的id有重复情况

解决方案

  1. 更改获取机器码的逻辑,把 hostname % 32 作为dataCenterId,ip % 32 作为 workerId

  2. 若获取机器信息失败,用 随机数作为兜底方案

    验证流程:

​ 1. qa验证生成id功能

​ 2. 线上发布后观察 dataCenterId 和 workerId 是否重复

​ 3. 线上压测模拟并发观察是否有主键冲突异常产生