分布式 ID 方案大盘点:从常见方案到美团 Leaf-Segment 的亮点
Hey,大家好,今天咱们来聊聊分布式 ID 这个分布式系统里绕不开的话题。分布式 ID 的设计目标是全局唯一、高效生成,还要尽量满足业务需求,比如有序性、可读性啥的。咱们先盘点一下常见的方案和具体的 ID 名称,再重点剖析美团的 Leaf-Segment 方案,看看它到底有啥过人之处。
常见分布式 ID 方案与具体名称
分布式 ID 方案五花八门,下面是几个常见的,带上它们的"学名"和特点:
-
UUID(通用唯一标识符)
- ID 名称 :UUID,比如
550e8400-e29b-41d4-a716-446655440000
。 - 特点:128 位,36 个字符(含连字符),基于随机数或时间 + MAC 地址生成。
- 优点:简单,生成不依赖网络,唯一性有保障。
- 缺点:太长、无序,存数据库占空间,索引效率低。
- ID 名称 :UUID,比如
-
数据库自增 ID
- ID 名称 :通常叫
Auto-Increment ID
,比如 MySQL 的id
列,1、2、3 这样递增。 - 特点:依赖数据库的 AUTO_INCREMENT,单表唯一。
- 优点:简单、有序,单机无敌。
- 缺点:分布式下单点压力大,扩展性差。
- ID 名称 :通常叫
-
Snowflake(雪花算法)
- ID 名称 :Snowflake ID,比如
690174210123456789
(64 位整型)。 - 特点:Twitter 提出的,结构是 41 位时间戳 + 10 位机器 ID + 12 位序列号。
- 优点:高效、有序,单机每毫秒能生成 4096 个。
- 缺点:依赖时钟,时钟回拨可能重复,机器 ID 得手动配。
- ID 名称 :Snowflake ID,比如
-
Redis 生成 ID
- ID 名称 :一般叫
Redis Incr ID
,比如10001
、10002
。 - 特点:用 Redis 的 INCR 命令递增生成。
- 优点:简单、高性能,支持分布式。
- 缺点:依赖 Redis,持久化得自己搞,宕机可能丢号。
- ID 名称 :一般叫
-
Zookeeper 生成 ID
- ID 名称 :常叫
Zookeeper Sequential ID
,比如znode-0000000001
。 - 特点:利用 Zookeeper 顺序节点生成。
- 优点:强一致性,有序。
- 缺点:性能一般,依赖 Zookeeper,适合低并发场景。
- ID 名称 :常叫
-
美团 Leaf
- ID 名称 :Leaf ID,分两种:
Leaf-Segment ID
(号段模式,如1000001
)和Leaf-Snowflake ID
。 - 特点:支持号段模式和 Snowflake 两种,灵活性高。
- 优点:高性能、低依赖,后文细聊。
- 缺点:号段模式可能浪费 ID,配置稍复杂。
- ID 名称 :Leaf ID,分两种:
美团 Leaf-Segment 方案:深度剖析与优势
美团的 Leaf 是开源的分布式 ID 生成框架,提供了两种模式:Leaf-Segment(号段模式)和 Leaf-Snowflake。咱们重点聊聊 Leaf-Segment,因为它在实际业务中用得特别多,尤其适合高并发场景。
Leaf-Segment 的工作原理
Leaf-Segment 的核心思路是"预分配号段":
- 号段预取 :服务启动时,从数据库(比如 MySQL)拿一段 ID 范围,比如
[1000, 2000)
。 - 内存生成:服务把这段号段加载到内存,客户端请求 ID 时直接从内存递增返回,比如 1000、1001、1002。
- 动态更新 :号段用完前,异步去数据库再拿一段,比如
[2000, 3000)
,无缝衔接。
数据库表大概长这样:
sql
CREATE TABLE leaf_segment (
biz_tag VARCHAR(32) PRIMARY KEY, -- 业务标签,比如 "order"
max_id BIGINT NOT NULL, -- 当前最大 ID
step INT NOT NULL, -- 号段步长,比如 1000
update_time TIMESTAMP -- 更新时间
);
INSERT INTO leaf_segment (biz_tag, max_id, step) VALUES ('order', 1000, 1000);
一个实际例子
假设订单系统用 Leaf-Segment:
- 服务 A 启动,拿
[1000, 2000)
。 - 客户端请求 100 个 ID,A 从内存返回 1000 到 1099。
- 快用完时(比如到 1900),异步请求新号段
[2000, 3000)
。 - 数据库里
max_id
更新为 3000,步长不变。
Leaf-Segment 的优势
-
高性能
- 为啥:ID 生成全在内存,走的是本地递增,基本没网络开销。
- 数据说话:美团实测 QPS 能到几万甚至更高,比直接读数据库快几百倍。
- 对比:Snowflake 每毫秒 4096 个上限,Leaf-Segment 靠步长调整,单机轻松破万。
-
低数据库依赖
- 为啥:号段用完才访问一次数据库,平时都不用连。
- 好处:数据库压力从"每次请求都访问"降到"几千次请求访问一次",抗住了高并发。
- 对比:数据库自增 ID 每条都得找数据库,QPS 一高就崩。
-
平滑扩容
- 为啥:每个服务独立拿号段,互不干扰,加节点不影响已有逻辑。
- 例子:订单系统从 3 台扩到 5 台,每台拿自己的号段,零迁移。
- 对比:Snowflake 扩容得调整机器 ID,配置麻烦。
-
支持业务隔离
- 为啥 :用
biz_tag
区分业务,订单用order
,用户用user
,号段互不冲突。 - 好处:一个系统支持多业务,ID 前缀还能加区分。
- 对比:UUID 和 Snowflake 没这种天然隔离,得自己拼字段。
- 为啥 :用
-
容错性强
- 为啥:数据库挂了,内存里的号段还能撑一阵。
- 例子:步长 1000,数据库宕机 1 分钟,高峰每秒 100 请求也能撑 10 秒。
- 对比:Redis 或数据库自增一旦挂,ID 生成直接停。
Leaf-Segment 的小瑕疵
当然,没啥是完美的:
- ID 不严格连续 :服务重启可能丢号段,比如
[1500, 2000)
没用完就浪费了。 - 步长配置:步长太大浪费 ID,太小频繁访问数据库,得调平衡。
- 部署稍复杂:得搭个服务管理号段,比 UUID 直接调 API 多点成本。
和其他方案对比
- VS UUID:Leaf-Segment 有序、短小,存数据库友好,UUID 太乱太长。
- VS Snowflake:Leaf-Segment 不怕时钟回拨,依赖更少,但 Snowflake 单机更简单。
- VS Redis:Leaf-Segment 持久化靠数据库更稳,Redis 得自己搞备份。
总结
分布式 ID 方案各有千秋,UUID 简单粗暴,Snowflake 高效有序,Leaf-Segment 则在性能和实用性上找到了甜蜜点。它的号段模式把数据库压力降到最低,又能扛高并发,还支持业务隔离,难怪美团拿它干大事。你们公司用啥方案生成 ID?有啥心得,欢迎留言聊聊!