点击标题下「蓝色微信名」可快速关注
我们的应用系统设计中可能会用到ID字段,有可能是通过数据库生成的,有可能是应用自己生成的,有可能是无业务含义的,有可能是夹杂着业务属性的,可能不同的场景,有着不同的生成方案。
技术社群的这篇文章《雪花算法ID重复了?惨痛生产教训:请勿轻易造轮子!》给我们讲解了应用设计ID的问题,提到的相关方案,对我们的工作可能有所帮助和指导。
原文如下,
最近线上系统发生了一个事件:订单号/流水号出现了重复,影响了核心业务流程。最终定位到根源:一个自研的二方包雪花算法ID生成器出现了问题。
下面我们来回顾一下雪花算法的标准结构,分析问题出在哪,总结一些通用的设计建议。
一、标准雪花算法(Snowflake)
标准的Snowflake ID由一个64位long型整数构成:
+----------------------------------------------------------------------------------------------------+
| 1 Bit | 41 Bits 时间戳 | 5 Bits 数据中心ID | 5 Bits 机器ID | 12 Bits 序列号 |
+----------------------------------------------------------------------------------------------------+优点:
二、我们的“定制版”雪花算法:问题在哪?
我们使用的二方包雪花算法结构如下(根据排查推测):
+----------------------------------------------------------------------------------------------------+
| 31 Bits 时间戳Delta | 13 Bits 数据中心ID | 4 Bits 工作ID | 8 Bits 业务ID | 8 Bits 序列号 |
+----------------------------------------------------------------------------------------------------+看起来字段丰富,但存在严重问题:
1、时间戳仅保留31位,最多支持24.85天!
2、BusinessId用的是IP最后一段
3、WorkId和DataCenterId未配置,全为0
最终结果:时间轮回 + IP冲突 + 序列重复,ID彻底撞车。
三、教训总结
通用组件不建议自研
不盲信二方包
合理设置机器ID
提前覆盖边界场景
四、推荐做法
使用成熟的开源实现,如Hutool、Baomidou等:
// Hutool 示例
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
long id = snowflake.nextId();
// Baomidou 示例(支持从 IP/MAC 自动推导,也可手动指定)
DefaultIdentifierGenerator generator = new DefaultIdentifierGenerator(1, 1); // workerId=1, dataCenterId=1
long id = generator.nextId("user");对于中大型系统,DataCenterId一般用来标识不同的机房或者AZ (Availability Zone)。
WorkerId的配置策略可以根据系统规模逐步演进:
随着系统规模扩大,推荐逐步引入更复杂但更稳妥的机制,避免一开始就过度设计。
五、其它建议:不要将业务标志拼入ID中
有时我们为了确保唯一性,会试图将业务信息(如类型前缀、模块编号)拼接进ID。但这种做法会带来一系列问题:
更稳妥的做法是将业务字段单独存储,ID仅用于唯一标识和排序。
六、结语
别为造轮子而造轮子,尤其在通用组件上,不可抱侥幸心理。
如果您认为这篇文章有些帮助,还请不吝点下文章末尾的"点赞"和"在看",或者直接转发朋友圈