写点什么

分布式集群中雪花 ID 重复?三招教你彻底避坑!实战经验 + 解决方案

作者:Geek_e3e86e
  • 2025-07-02
    湖南
  • 本文字数:1469 字

    阅读完需:约 5 分钟

背景:一次离奇的主键重复事故

某天线上日志偶尔报错: “主键重复” 。一个看似简单的业务场景——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 等方案实现多级容灾?

用户头像

Geek_e3e86e

关注

还未添加个人签名 2025-03-07 加入

还未添加个人简介

评论

发布
暂无评论
分布式集群中雪花ID重复?三招教你彻底避坑!实战经验+解决方案_Java_Geek_e3e86e_InfoQ写作社区