造轮子,还是买现成的
前九篇把原理讲透了:号段双 buffer、雪花时钟、workerID 分配、Redis 两本账。老王回头一看,团队真要手搓一套发号服务,光是 workerID 分配和回拨治理就得写两周——这还是「能跑」的水平,不是「可靠」的水平。他决定先看开源:美团 Leaf、百度 UidGenerator、滴滴 Tinyid,三家大厂各自踩过坑后沉淀的方案。逐个拆。
Leaf:双模式任选的美团方案
Leaf 是三家里功能最全的,一个 jar 包里装了两套引擎,按业务口味切换:
segment 模式:就是第 3、4 篇的 DB 号段 + 双 buffer 落地版。每个业务一个 biz_tag,内存双 buffer 异步预加载。它比原理篇多做了一件事——step 动态调整:根据每个号段的实际消耗速度,把号段长度动态缩放(默认在 10 分钟 QPS 到 100 万 QPS 之间伸缩),流量低谷少占号,高峰自动扩容,DB 压力更平滑。
snowflake 模式:第 5-8 篇的雪花落地版。workerID 交给 ZooKeeper 顺序节点自动分配,解决了「镜像克隆撞号」的人工隐患。但它有个明确的弱项——时钟回拨只告警不兜底:检测到回拨直接抛异常,不等待不切换。也就是说 snowflake 模式的回拨治理,Leaf 把难题留给了你(第 7 篇的三招得自己补)。
接入形态是 Java SDK,应用引入 jar 直连 DB,没有独立发号服务——少一个部署单元,也意味着每个应用都要配号段库的连接。
UidGenerator:RingBuffer 预生成的百度方案
UidGenerator 是雪花改良派。它先动了位分配:64 位切成 1 位符号 + 28 位秒级时间戳 + 22 位 workerID + 13 位序列。秒级时间戳只够用 8.5 年(毫秒级能撑 69 年),换来的是 workerID 手笔阔绰——22 位支持约 420 万节点。每秒序列 8192 个,中等流量绰绰有余,超出就等下一秒,反正秒级单位下等待的感知更粗。
真正的杀手锏是 CachedUidGenerator:启动时把未来一段时间的 ID 批量预生成进 RingBuffer 环形数组,取号就是从内存读一个指针位置,官方压测单机 600 万/s。代价要想清楚两点:一是 RingBuffer 里预生成的 ID,其时间位代表的是「生成时刻」而非「业务领取时刻」,按时间范围反查业务时会和领取时间有偏差;二是重启会丢弃 RingBuffer 里未用完的号,洞比双 buffer 更大。
依赖极简:纯 Java 实现,workerID 用 DB 表或配置分配即可,没有 ZooKeeper。
Tinyid:轻装上阵的滴滴方案
Tinyid 是号段阵营的极简派,只做 segment 模式,但给了两种接入形态:HTTP 方式,任何语言一个 GET 请求拿号,多语言团队的最爱;Java SDK 方式,本地缓存号段异步预加载,发号不出网。它的特色是接口设计——一次调用可以取一个号段、可以批量取 N 个号,号段长度还能按业务动态配置。
代价是没有雪花模式:强依赖 DB,DB 挂了号段打光就停服(降级方案见第 14 篇)。对纯粹要「数据库号段」的场景,它比 Leaf 更轻,代码量小到可以整个读完再决定要不要用。
横评一张表
| 维度 | Leaf segment | Leaf snowflake | UidGenerator | Tinyid |
|---|---|---|---|---|
| 发号模式 | DB 号段双 buffer | 雪花 | 雪花改良 + RingBuffer | DB 号段 |
| 单调/趋势 | 趋势递增 | 毫秒级趋势 | 秒级趋势 | 趋势递增 |
| 时钟依赖 | 无 | 有,回拨仅告警 | 有,秒级较粗 | 无 |
| 外部依赖 | DB | DB + ZK | DB(分配 workerID) | DB |
| 吞吐量级 | 千万级/s(本地号段) | 单机百万/s | 单机 600 万/s | SDK 千万级/s |
| 多语言 | 不友好(Java SDK) | 不友好 | 不友好 | HTTP 友好 |
决策树一锤定音
老王把选型画成四个问题,按序问下去:
一问:要严格单调吗?三个开源全是「趋势」不是「严格」,严格单调请回到集中式号段 + 单点,或者干脆用 DB 自增。
二问:团队是纯 Java 吗?不是,Tinyid 的 HTTP 形态直接入围;是,往下问。
三问:能接受洞和乱序吗?能,且要极致单机吞吐,UidGenerator Cached 上桌;不能,往叶子走。
四问:时钟运维靠谱吗?靠谱且机器规模可控,Leaf snowflake(补上第 7 篇回拨治理);不靠谱,Leaf segment——号段阵营对时钟零依赖,这正是它经久不衰的原因。
四个问题都落空再谈自研。不过提醒一句:Leaf segment 的核心逻辑不到千行,读完源码再「自研」,多半会发现最后写出来的是个 Leaf 换皮——那就直接用 Leaf。
小结
三大开源一句话:Leaf 双模式功能全,snowflake 回拨弱项要自己补;UidGenerator 吞吐之王,RingBuffer 的洞和时间语义要想清;Tinyid 轻量多语言,纯号段场景性价比最高。选型落定,还有一个隐蔽话题没聊:同样是趋势递增,为什么有的 ID 插表丝滑、有的却把 B+ 树搅得稀碎?下一篇专讲趋势递增的艺术。
评论 (0)