分布式集群中雪花ID重复?三招教你彻底避坑!实战经验+解决方案
0 | 时间戳(41位) | 数据中心ID(5位) | 机器ID(5位) | 序列号(12位)确保workId在集群内绝对不重复。时钟回拨、IP变更等场景需有兜底策略。关键节点增加日志监控,如workId生成过程。
背景:一次离奇的主键重复事故
某天线上日志偶尔报错: “主键重复” 。一个看似简单的业务场景——APP持续上传信息,用户量不足1W,并发量极低,仅涉及单表插入操作。
然而,就是这个简单的insert,却让团队排查到怀疑人生……
问题定位:
项目基于SpringCloud+MybatisPlus,主键默认使用雪花ID(Snowflake)。排查发现,生产环境部署了分布式集群(A/B/C多台机器),但未配置workId。
最终结论:多台机器因workId相同,导致生成的雪花ID发生碰撞。
知识卡:什么是雪花ID?
核心原理
Snowflake是Twitter开源的分布式ID生成算法,生成64位Long型ID,结构如下:
0 | 时间戳(41位) | 数据中心ID(5位) | 机器ID(5位) | 序列号(12位)
优点
-
高性能: 单机每秒可生成26万+ ID。
-
趋势递增: 整体有序,适合数据库索引。
-
去中心化: 无需依赖
Redis/Zookeeper
,本地生成。
致命缺点
-
时钟回拨: 服务器时间倒退会导致ID重复。
-
机器ID冲突: 分布式环境下,若workId重复,ID必然重复。
-
ID不连续: 高并发时可能出现“跳号”。
避坑指南:如何确保workId全局唯一?
方案1:IP动态计算(推荐)
利用服务器IP最后一段取模,自动分配workId:
// 示例代码:根据IP生成workId
String hostAddress = InetAddress.getLocalHost().getHostAddress();
int ipLastSegment = Integer.parseInt(hostAddress.split("\.")[3]);
return ipLastSegment % 32; // 确保workId在0-31范围内
-
优点: 无需人工干预,IP天然唯一。
-
注意: 需确保IP末段不重复,适用于静态IP环境。
方案2:环境变量注入(Docker友好)
通过启动命令注入workId:
# Docker启动示例
docker run -e WORKER_ID=2 -e DATACENTER_ID=1 your-service-image
适用场景: 容器化部署,灵活可控。
方案3:中间件托管(高可用场景)
使用Redis或配置中心(如Nacos)维护workId映射表:
Key: service-name@ip → Value: workId
优点: 适合动态扩缩容,避免IP变化引发问题。
实战代码:MybatisPlus动态配置workId
@Configuration
publicclassMybatisPlusConfig{
@Bean
public IdentifierGenerator identifierGenerator(){
returnnew DefaultIdentifierGenerator(getWorkerId(), getDatacenterId());
}
// 核心逻辑:优先从环境变量获取,否则IP计算
privatelonggetWorkerId(){
try {
String workerIdStr = System.getenv("WORKER_ID");
if (workerIdStr != null) return Long.parseLong(workerIdStr);
String hostAddress = InetAddress.getLocalHost().getHostAddress();
int ipLastSegment = Integer.parseInt(hostAddress.split("\\.")[3]);
return ipLastSegment % 32;
} catch (Exception e) {
log.error("Get workId failed, fallback to default 1");
return1L; // 兜底策略
}
}
// 数据中心ID同理(略)
}
代码要点:
-
环境变量优先级 > IP计算 > 默认值兜底。
-
日志告警:IP获取失败时需人工介入。
总结:分布式ID设计的核心原则
-
全局唯一: 确保workId在集群内绝对不重复。
-
容错机制: 时钟回拨、IP变更等场景需有兜底策略。
-
可观测性: 关键节点增加日志监控,如workId生成过程。
延伸思考:
若服务规模超32台(5位workId上限),如何扩展?
如何结合Leaf、UUID等方案实现多级容灾?
更多推荐
所有评论(0)