目 录CONTENT

文章目录

微服务全局唯一 ID 避坑指南:5 种方案实测 + 3 步选型法

冰点
2025-09-25 / 0 评论 / 0 点赞 / 3 阅读 / 10550 字

大家好,我是冰点。

最近在微服务重构项目里踩了个大坑:订单服务和库存服务用了不同的 ID 生成规则,同步数据时发现 “ID 冲突”—— 两个不同的订单居然用了同一个 ID,导致库存扣减错乱,排查了 3 小时才定位到问题。

这其实是微服务开发的常见痛点:单体应用时用数据库自增 ID 就行,但拆分后多服务、多数据库分布式部署,必须要有一套 “全局唯一、格式统一、能溯源” 的 ID 方案。今天就结合实战经验,聊聊微服务体系下全局唯一 ID 的 5 种实现方案,以及怎么选才不会踩坑。

一、先搞懂:微服务对全局唯一 ID 的 4 个核心要求

在讲方案前,得先明确 “好的全局 ID” 要满足什么条件,不然很容易选到不合适的方案:

  1. 绝对唯一:这是底线,不管多少服务、多少节点,生成的 ID 不能重复,比如订单 ID、用户 ID 绝不能冲突;

  1. 趋势递增:很多场景需要 ID 按时间排序,比如订单表按 ID 分区、日志按 ID 查询,无序 ID 会导致索引失效、查询变慢;

  1. 高性能低延迟:高并发场景下(比如秒杀),ID 生成不能成为瓶颈,最好能单机每秒生成几十万甚至上百万个 ID;

  1. 可溯源:能从 ID 里拿到关键信息,比如 “这个 ID 是哪个服务、哪个节点在什么时间生成的”,方便排查问题。

满足这 4 点,才算得上合格的微服务全局 ID 方案。接下来逐个拆解常见方案,看看它们各自的表现。

二、5 种全局唯一 ID 实现方案:原理 + 优缺点 + 实战场景

方案 1:数据库自增 ID(最简单但最容易踩坑)

原理:用一个专门的 “ID 生成库”,创建一张global_id表,靠数据库自增主键(AUTO_INCREMENT)生成唯一 ID。比如:

CREATE TABLE global_id (
  id BIGINT NOT NULL AUTO_INCREMENT COMMENT '全局唯一ID',
  service_name VARCHAR(64) NOT NULL COMMENT '生成ID的服务名',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (id)
);

微服务需要 ID 时,就向这张表插入一条记录,返回的id就是全局唯一 ID。

实战踩坑经历:之前做用户服务时先用了这个方案,结果大促期间并发一上来,所有服务都去抢这张表的自增 ID,数据库直接成了瓶颈 —— 插入请求排队,ID 生成延迟从 1ms 涨到了 500ms,导致用户注册接口超时。

优缺点:

  • 优点:实现简单,不用写复杂代码,直接用数据库自带功能;

  • 缺点:单点风险(ID 生成库挂了全服务瘫痪)、性能低(并发超 1000 就容易卡)、不可溯源(ID 里没有服务和时间信息)。

适用场景:并发量低(比如日均请求 10 万以内)、业务简单的微服务,比如后台管理系统的配置项 ID。

方案 2:UUID/GUID(最方便但最不推荐)

原理:用 Java 自带的java.util.UUID生成 36 位字符串 ID,比如550e8400-e29b-41d4-a716-446655440000,基于 MAC 地址、时间戳、随机数组合生成,理论上全球唯一。

为什么不推荐:之前在日志服务里用过 UUID,结果踩了两个大坑:

  1. 无序且占空间:UUID 是随机字符串,没法按时间排序,日志表用 UUID 当主键,索引碎片化严重,查询速度比自增 ID 慢 3 倍;

  1. 无法溯源:从 UUID 里看不到任何信息,一旦出现问题,根本不知道这个日志是哪个服务生成的。

优缺点:

  • 优点:生成简单(一行代码UUID.randomUUID().toString())、无网络依赖(本地生成)、性能高;

  • 缺点:无序、占空间(36 位比 64 位整数占更多存储)、不可溯源、不适合作为数据库主键。

适用场景:不需要排序、仅用于临时标识的场景,比如缓存 Key、请求追踪 ID(Trace ID)。

方案 3:雪花算法(Snowflake)—— 微服务主流方案

原理:这是 Twitter 开源的方案,把 64 位 Long 型 ID 分成 4 段,像 “雪花” 一样包含关键信息:

  • 1 位符号位:固定为 0,保证 ID 是正数;

  • 41 位时间戳:记录生成时间(精确到毫秒),能覆盖约 69 年(2^41/1000/60/60/24/365≈69);

  • 10 位机器位:分成 “5 位服务 ID+5 位节点 ID”,能支持 32 个服务、每个服务 32 个节点(共 1024 个节点);

  • 12 位序列号:每个节点每毫秒能生成 4096 个 ID(2^12=4096),解决同一毫秒内的并发问题。

比如生成的 ID1326598478654234625,解析后能知道:“这是服务 ID=5、节点 ID=8 的服务,在 2024 年 5 月 20 日 14:21:18 生成的第 123 个 ID”。

实战优化:我们团队在雪花算法基础上做了两个优化:

  1. 时间戳偏移:用 “当前时间戳 - 项目启动时间戳” 代替绝对时间戳,减少 ID 长度,同时避免时间回拨问题(比如服务器时钟不准导致时间倒退,会生成重复 ID);

  1. 机器位动态配置:通过配置中心动态分配服务 ID 和节点 ID,避免手动配置冲突,比如订单服务分配服务 ID=10,库存服务分配服务 ID=11。

优缺点:

  • 优点:性能高(单机每秒生成百万级 ID)、趋势递增、可溯源(含服务和时间信息)、无依赖(本地生成);

  • 缺点:依赖服务器时钟,时钟回拨会导致 ID 重复(需要做时钟校验)、机器位固定后扩展节点受限(比如 10 位机器位最多支持 1024 个节点)。

适用场景:绝大多数微服务场景,尤其是高并发场景(比如秒杀、订单、支付),是目前行业内的主流方案。

方案 4:Redis 自增(高可用但有网络依赖)

原理:利用 Redis 的INCR或INCRBY命令的原子性,生成自增 ID。比如:

  • 给每个服务分配独立的 Key,比如订单服务用order:global:id,用户服务用user:global:id;

  • 服务需要 ID 时,执行INCR order:global:id,返回的结果就是唯一 ID;

  • 为了避免 Redis 重启后 ID 重置,需要开启 RDB 或 AOF 持久化,还可以用 Redis 集群保证高可用。

实战案例:之前做秒杀服务时用了 Redis 自增,配合集群部署:

  • 主节点负责生成 ID,从节点同步数据,主节点挂了从节点自动切换;

  • 给 ID 加了 “前缀标识”,比如订单 ID 前缀是10,生成的 ID 是101684523698745(前缀 10+Redis 自增数),方便溯源。

优缺点:

  • 优点:性能高(Redis 单机每秒支持 10 万 + INCR 操作)、支持分布式扩展、可按服务分 Key 避免冲突;

  • 缺点:依赖 Redis 服务(网络延迟比本地生成高)、Redis 集群配置复杂、持久化失败会导致 ID 重复(比如主从切换时数据不一致)。

适用场景:需要跨服务共享 ID、且能接受轻微网络延迟的场景,比如秒杀活动的商品 ID、优惠券 ID。

方案 5:分布式 ID 生成器(成熟但有学习成本)

原理:用现成的开源组件,比如美团的 Leaf、百度的 UidGenerator、滴滴的 TinyID,这些组件封装了多种 ID 生成策略(比如 Leaf 支持雪花算法和数据库分段自增),还解决了时钟回拨、节点扩展等问题。

以美团 Leaf 为例,它的 “数据库分段自增” 方案很适合中小团队:

  1. 在数据库里存 “服务 - 当前 ID 段 - 步长”,比如订单服务当前 ID 段是 1-10000,步长 10000;

  1. Leaf 服务提前把 1-10000 的 ID 段加载到内存,生成 ID 时直接从内存取,用完后再去数据库取下一个段(10001-20000);

  1. 这样既避免了频繁访问数据库,又解决了单点问题(Leaf 服务可集群部署)。

实战体验:现在我们项目用的是 Leaf,有两个明显优势:

  1. 开箱即用:配置好数据库和服务名,启动就能生成 ID,不用自己写雪花算法的时钟校验逻辑;

  1. 监控完善:能看到每个服务的 ID 生成量、剩余 ID 段数量,快用完时会报警,避免 ID 耗尽。

优缺点:

  • 优点:成熟稳定、解决了常见坑(时钟回拨、ID 耗尽)、支持多种生成策略、有监控告警;

  • 缺点:需要部署和维护额外组件、有学习成本(比如理解 Leaf 的分段策略)。

适用场景:中大型微服务架构,尤其是服务数量多、并发高、需要长期稳定运行的场景,比如电商平台、金融系统。

三、实战选型指南:3 步选对全局 ID 方案

很多开发者纠结 “到底选哪个方案”,其实不用死磕 “最优解”,按这 3 步就能快速匹配:

第一步:看并发量

  • 低并发(日均 10 万以内):选数据库自增 ID(简单);

  • 中高并发(日均 100 万 - 1 亿):选雪花算法(本地生成无延迟);

  • 超高并发(日均 1 亿以上):选 Redis 自增或分布式 ID 生成器(支持集群扩展)。

第二步:看是否需要溯源

  • 需要溯源(比如订单、支付):选雪花算法或分布式 ID 生成器(含服务、时间信息);

  • 不需要溯源(比如缓存 Key、临时标识):选 UUID(方便)。

第三步:看团队成本

  • 小团队(3 人以内):优先选雪花算法(不用维护额外组件);

  • 中大型团队:选分布式 ID 生成器(成熟稳定,有监控)。

四、避坑总结:这 3 个错误别再犯

  1. 不要混用不同方案:比如订单服务用雪花算法,库存服务用 UUID,同步数据时必然出问题;

  1. 不要忽略时钟回拨:用雪花算法一定要加时钟校验,比如检测到当前时间比上次生成 ID 的时间小,就暂停生成并报警;

  1. 不要过度设计:中小团队没必要一上来就部署 Leaf 集群,先用雪花算法满足需求,后续再根据业务增长优化。

其实全局唯一 ID 的核心不是 “技术多复杂”,而是 “匹配业务需求”。比如我们项目从 “数据库自增” 过渡到 “雪花算法”,再到现在用 “Leaf”,就是跟着业务并发量和团队规模一步步调整的。

如果你们在项目里用过其他 ID 方案,或者有踩坑经历,欢迎在评论区分享,咱们一起避坑~

0

评论区