美团 Leaf 号段模式(Leaf-Segment)是一种基于数据库批量预分配 ID 区间的分布式 ID 生成方案,核心是内存发号、异步预取、双Buffer 保障,实现高并发、低延迟、全局趋势递增。
一、核心原理
1. 核心思想
- 每个 Leaf 服务节点启动时,从数据库批量获取一个 ID 号段(如
[1000, 2000)),缓存在本地内存。 - 业务请求 ID 时,直接从内存中原子递增分配,不直接访问数据库,大幅提升性能。
- 号段用尽前,异步预取下一个号段,实现无缝切换。
2. 数据库表结构(核心)
CREATE TABLE `leaf_alloc` (
`biz_tag` varchar(64) NOT NULL COMMENT '业务标识',
`max_id` bigint(20) NOT NULL DEFAULT '1' COMMENT '当前最大ID',
`step` int(11) NOT NULL COMMENT '号段步长(每次申请数量)',
`description` varchar(256) DEFAULT NULL COMMENT '业务描述',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`biz_tag`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
biz_tag:业务唯一标识(如order_id、user_id),不同业务独立号段。max_id:当前已分配的最大 ID,通过UPDATE原子更新。step:每次申请的 ID 数量(如 2000),决定数据库访问频率。
3. 双 Buffer 机制(关键优化)
Leaf 使用两个号段缓冲区(当前号段、预备号段),解决号段切换时的阻塞问题:
- 服务加载当前号段 A,开始发号。
- 当 A 消耗到**10%(可配置)**时,异步预取号段 B。
- A 用尽后,立即切换到 B,同时异步预取下一个号段 C。
- 循环往复,实现无感知、无阻塞的号段切换。
4. 动态步长(自适应)
Leaf 会根据号段消耗速度自动调整 step:
- 若号段消耗过快(如 < 15 分钟),增大 step,减少数据库访问。
- 若消耗过慢,减小 step,避免 ID 浪费。
二、工作流程(极简版)
- 初始化:Leaf 服务启动,根据
biz_tag查询数据库,获取max_id和step,生成当前号段[max_id, max_id+step)。 - 发号:业务调用
get(biz_tag),从内存中原子递增返回 ID。 - 预取:当前号段剩余 < 10% 时,异步执行 SQL:
并加载新号段到预备 Buffer。UPDATE leaf_alloc SET max_id = max_id + step WHERE biz_tag = 'order_id'; - 切换:当前号段用尽,立即切换到预备号段,重复步骤 2。
三、核心特性
- 全局唯一:通过数据库原子更新
max_id,保证号段不重复。 - 趋势递增:ID 整体单调递增,适合 MySQL InnoDB 主键索引。
- 高并发:内存发号,单机 QPS 可达 5W+,TP99 < 1ms。
- 高可用:双 Buffer 可容忍数据库短时间宕机(号段未用尽前不影响发号)。
- 业务隔离:按
biz_tag独立管理,不同业务互不干扰。 - 低浪费:步长动态调整,减少 ID 闲置浪费。
四、优缺点对比
| 优点 | 缺点 |
|---|---|
| 性能极高,内存操作 | 依赖数据库(单点风险,需主从) |
| 趋势递增,索引友好 | 号段切换瞬间可能有 ID 空洞 |
| 容忍数据库短暂不可用 | 需维护数据库与 Leaf 集群 |
| 接入简单,支持多业务 | 水平扩展需依赖数据库原子更新 |
五、适用场景
- 订单 ID、用户 ID、支付流水号等需要趋势递增、全局唯一的场景。
- 高并发、低延迟要求的电商、金融、外卖等业务。
- 对 ID 连续性要求不苛刻,但需高性能、高可用的分布式系统。
六、与 Snowflake 模式的区别
- Leaf-Segment:依赖数据库,趋势递增、无时钟问题,适合强依赖有序 ID 的场景。
- Leaf-Snowflake:纯内存、无数据库依赖,ID 带时间戳,但需处理时钟回拨。
注意:本文归作者所有,未经作者允许,不得转载