面试总被问分布式ID? 十分钟带你读懂美团 Leaf 号段模式

一个订单号而已,有什么难的?难到美团专门做了个系统来伺候它。今天聊聊它最朴素的思路------一次取一段号。


先说说,"发号"到底难在哪

任何系统都得给数据编个号:订单号、支付流水号、优惠券号......听着简单,数据库自增不就完了?

问题出在量上。当一个系统每秒要发出几万个号,而且要求:

  • 每个号全球唯一,不能重复;
  • 号一路往上走(这样存进数据库才顺畅、不卡顿);
  • 系统几乎不能挂------一年 365 天,总共宕机不能超过 5 分钟。

这时候,让数据库"来一个请求就现编一个号",就彻底扛不住了。传统的做法是每取一个号就读写一次数据库,三个毛病:

  1. 慢:所有请求都压在那一台数据库上;
  2. 脆:数据库一挂,全系统都发不出号;
  3. 难扩容:机器一多,改配置能把你改崩溃。

核心思想:别一个个取,一次取一段

美团想了个特朴素的招。打个比方:

你每次去银行柜台取一个号,跑断腿;不如一次性取回一整本号,回家慢慢用。

Leaf 号段模式就是这么干的------数据库只记一件事: "我已经发到第几号了" ​。Leaf 服务定期去数据库一次性搬走一整段号(比如 1000 个),剩下的,全在内存里发,跟数据库就没关系了。

数据库里,每个业务就一行记录:

sql

lua 复制代码
biz_tag   -- 业务名:订单、支付、券,各用各的,互不干扰
max_id    -- 已经发到第几个号了
step      -- 每次搬多少个号

搬号的时候,一步到位(数据库里这叫"事务",意思是要么全成、要么全不成):

sql

vbnet 复制代码
UPDATE leaf SET max_id = max_id + step WHERE biz_tag = 'xxx';
SELECT biz_tag, max_id, step FROM leaf WHERE biz_tag = 'xxx';

第一句把"已发到 3000"改成"已发到 4000",第二句把这 1000 个号领走。之后 1000 次请求,全在内存里瞬间完成,数据库碰都不碰。

而且这个方案加机器特别简单:一号机领 1,1000,二号机领 1001,2000,互不打架。想扩容,加机器就行。

灵魂设计:双 Buffer

号段模式爽是爽,但有个要命的瞬间------号段用完的那一刻。

如果只有一个缓冲区,用完时请求线程必须停下来干等数据库返回。网络一抖、撞上个慢查询,所有请求全堵在那儿排队,最慢的那批请求延迟会突然蹿高,用户体验瞬间崩掉。

Leaf 的解法是双 Buffer,规则就两条:

  1. 当前号段用到 10% ,后台线程偷偷异步去取下一段;
  2. 当前号段用完,下一段早就备好了,无缝切换。

关键点:发号请求从头到尾不碰数据库,等待的活儿全丢给了后台线程。

🎁 意外收获 :号段长度设成 高峰 QPS × 600(约 10 分钟的量),就算数据库整个挂掉,内存里已经取好的号还能让系统照常发号 10~20 分钟。

动态步长:一个恒等式搞定

双 Buffer 上线后,新问题又来了:

  • step 写死,流量涨 10 倍,容灾时间从 10 分钟缩到 1 分钟;
  • 流量太小,号段用不完,浪费一大截。

Leaf 的答案是恒等式:

text

ini 复制代码
Q × T = L
Q = 发号 QPS    T = 号段更新周期    L = 号段长度

与其写死 L,不如让 L 跟着 Q 自适应,把 T 稳定在理想区间:

  • 号段 15 分钟内 耗尽 → step × 2,翻倍;
  • 号段 30 分钟 还没用完 → step ÷ 2,减半;
  • 落在 15~30 分钟之间 → 维持不变。

于是"DB 宕机容忍窗口"永远稳定在 10~20 分钟,号段浪费也降到最低。


它不是银弹 ⚠️

边界要拎清楚:

  1. ID 可被推算------竞对拿两天订单号相减就能估你的单量,敏感场景得换雪花模式;
  2. 多实例只保证"趋势递增" ,不保证严格单调;
  3. 强依赖 DB------窗口拉长拉稳了,但 DB 长时间不可用,最终还是停摆。

一句话选型:号段模式 适合订单库主键、消息 ID 等内部场景;雪花模式适合对抗爬虫、防推算的敏感场景(代价是得处理时钟回拨)。


总结

号段模式没有高深算法,就是用最简单的表 + 一个事务 + 两个 Buffer + 一个恒等式,把单台 MySQL 的价值榨到极限:

一台 4 核 8G 的普通服务器,就能做到每秒 5 万次发号,最慢的请求也就 1 毫秒出头。

做架构,很多时候不是"堆更多机器",而是换个思路。把数据库从"频繁逐次读写"里解放出来,让它只做"定期批量记账"------这就是号段模式的精髓。


参考资料 :美团技术博客《Leaf------美团点评分布式ID生成系统》;开源仓库 Meituan-Dianping/Leaf

如果这篇文章帮到了你,点个赞 👍、收个藏 ⭐、关注我,后面持续更新分布式系统的硬核拆解。评论区聊聊你在发号/主键设计上踩过的坑?👇

相关推荐
方方洛18 小时前
ray教程-00-前言与导读
人工智能·分布式·机器学习
方方洛19 小时前
ray教程-01-认识Ray
人工智能·分布式·机器学习
yexianglunbai1 天前
RabbitMQ 详解:从入门到实战
分布式·rabbitmq
此时不提桶,更待何时1 天前
06-16-B-Kafka面试与生产事故实战
分布式·面试·kafka
mesyinjinghan1 天前
半导体 MES 与 ERP 的接口边界:六类单据该留在哪一层
分布式·eap·半导体mes
codigger1 天前
Redis 正式接入 AI:当"最懂速度的数据库"开始解决"记忆问题"
redis·分布式·后端·ai·向量检索
樱花落木兰1 天前
Redisson 可重入分布式锁底层原理详解
分布式
此时不提桶,更待何时2 天前
06-12-A-Kafka存储深水区与源码解析详解
分布式·kafka·linq
此时不提桶,更待何时2 天前
06-15-A-Kafka生态集成与流处理详解
分布式·kafka