雪花、号段、Leaf 三种分布式 ID:我们日增 2 亿数据的表,最终换掉了雪花
三年前我们的订单 ID 用的是雪花算法Snowflake跑了两年一直没问题。直到业务量冲到日增 2 亿条机器扩容、容器频繁重建时钟回拨开始密集出现订单 ID 出现重复对账直接崩了。那次之后我们系统性对比了雪花、号段、Leaf 三种方案最后切到了 Leaf-segment。这篇把三种方案的原理、代码、各自的坑讲清楚并附上我们选型时的真实数据。为什么不能用数据库自增先说为什么需要分布式 ID分库分表后单库自增 ID 会撞车两个库都从 1 开始而且自增暴露了业务量竞争对手能靠订单号猜你一天卖多少。分布式 ID 一般要求全局唯一、趋势递增BTree 索引友好、高性能、最好带时间信息。// 不推荐分库分表后自增 ID 直接撞车 TableId(type IdType.AUTO) // 1. 单库自增分到库2又从1开始 private Long id; // 库1 插入 id1库2 也插入 id1 - 主键冲突或跨库乱序逐行第 1 行IdType.AUTO依赖数据库自增分库后每个库各自计数库间必然重复且无法跨库排序。所以必须上分布式 ID 生成器下面三种是主流。雪花算法64 位里塞进时间、机器、序号雪花把 64 位 long 拆成1 位符号恒 0 41 位时间戳 10 位机器 ID 12 位序列号。同一毫秒内靠序列号区分不同毫秒靠时间戳递增。// 雪花算法核心简化版便于理解 public synchronized long nextId() { long timestamp System.currentTimeMillis(); // 1. 当前毫秒 if (timestamp lastTimestamp) { // 2. 时钟回拨 - 直接抛异常 throw new RuntimeException(时钟回拨拒绝生成); } if (timestamp lastTimestamp) { sequence (sequence 1) 4095; // 3. 同一毫秒序列号112位上限4095 if (sequence 0) { // 4. 本毫秒用尽等下一毫秒 timestamp waitNextMillis(lastTimestamp); } } else { sequence 0; // 5. 新毫秒序列号归零 } lastTimestamp timestamp; return ((timestamp - EPOCH) 22) // 6. 时间戳左移22位1012 | (workerId 12) // 7. 机器ID左移12位 | sequence; // 8. 拼上序列号 }逐行第 2 行处理时钟回拨——这是雪花最大的雷下面专门讲第 3 行同一毫秒内序列号递增最多 4096 个/毫秒第 6-8 行把三段按位拼成 64 位 long。单机能做到约 400 万 ID/秒4096 × 1000性能足够。但它依赖机器时钟单调而时钟回拨在容器化环境里太常见了。我们那天翻车的具体场景K8s 节点重启后NTP 把系统时间往回拨了 3 秒多个 Pod 的 workerId 又因为配置失误重复结果生成了和 3 秒前完全相同的 ID两笔订单主键冲突下游对账直接报错停服。时钟回拨 workerId 重复是雪花的两大杀手。号段模式一次取一段本地慢慢发号段模式segment的思路是不在每次生成时访问 DB而是从 DB 一次性申请一段区间比如 [1, 1000]在本地内存里挨个发发完了再去 DB 取下一段。DB 压力从每次一条降到每 1000 条一次。// 号段模式核心逻辑简化 class Segment { private long cur; // 1. 当前已发到哪 private long max; // 2. 本段上限从 DB 申请的 maxId private long step 1000; // 3. 每次申请 1000 个 } public synchronized long nextId() { if (cur max) { // 4. 本段用尽去 DB 取下一段 // UPDATE id_segment SET max_id max_id step WHERE biz order // 拿到新区间 [oldMax, newMax] cur dbOldMax 1; max dbNewMax; } return cur; // 5. 本地自增返回无 DB 交互 }逐行第 4 行号段用尽才去 DB 申请下一段平时nextId()只是内存自增性能极高、不依赖时钟第 5 行返回并自增。号段的优势是不怕时钟回拨ID 来自 DB 自增区间缺点是一旦 DB 挂了、当前段又正好发完就生成不了——可用性受 DB 约束。另外号段有号浪费服务重启没发完的号段就丢了但这是可接受的代价。Leaf把两种方案都做成服务美团开源的 Leaf 把上面两种做成可切换的服务。我们用的是Leaf-segment它在号段基础上加了双 buffer——当前段用到一定比例比如 10%就异步去预取下一个段避免段用尽才去 DB那一下卡顿。// Leaf-segment 双 buffer逻辑还原 class SegmentBuffer { Segment[] segments new Segment[2]; // 1. 两段缓冲 int currentPos 0; // 2. 当前在用哪段 boolean nextReady false; // 3. 下一段是否已预取好 } public long nextId() { Segment cur segments[currentPos]; if (cur.cur cur.max) { // 4. 当前段用完切到下一段 if (nextReady) { // 5. 下一段已就绪直接切换 currentPos ^ 1; nextReady false; asyncLoadNext(); // 6. 异步预取下下一段 } else { Thread.sleep(1); // 7. 还没就绪短暂等待极少触发 } } return segments[currentPos].nextId(); }逐行第 3 行nextReady标记下一段是否备好第 5 行当前段用尽且下一段已就绪就无缝切换第 6 行切换后立刻异步去取新的下一段保证永远有一段在内存候着。这把号段的段用尽卡顿彻底抹平了。我们压测时单实例 Leaf-segment 轻松到 5 万 ID/秒且 DB 每秒只被访问几次。Leaf 还提供Leaf-snowflake用 ZooKeeper/雪花 workerId 分配 时钟回拨时的借号策略缓解雪花痛点回拨一小段时间内用扩展位发号、记录回拨时长告警。但 workerId 仍需外部协调我们没选它。三种方案横向对比维度雪花 Snowflake号段 SegmentLeaf-segment趋势递增是按毫秒是是依赖时钟强依赖回拨致命不依赖不依赖性能极高纯本地极高本地偶尔DB极高双 buffer 平滑可用性依赖仅需本地时钟依赖 DB 段未耗尽依赖 DB 段未耗尽部署复杂度低嵌应用中需 DB 表中高独立服务 DB主要风险时钟回拨、workerId 冲突DB 挂段耗尽、号浪费DB 挂段耗尽、号浪费一句话雪花最轻量但最怕时钟回拨号段/Leaf 不怕时钟但依赖 DB 且不轻量。如果你的机器时钟可信、部署稳定雪花够用一旦上容器、频繁扩缩容雪花的风险会非线性放大。我的取舍日增 2 亿我们选 Leaf-segment我的观点很直接容器化、高并发、对 ID 重复零容忍的业务别用裸雪花。我们当初坚持用雪花是嫌 Leaf 要起服务、多一个依赖结果时钟回拨那次停服 2 小时损失比多运维一个 Leaf 服务大得多。切到 Leaf-segment 后我们做了三件事独立部署 Leaf 集群至少 2 个节点 DB 主从ID 生成不再和业务的容器生命周期绑定容器重建不影响发号DB 段表单独库不和业务库混避免业务库抖动拖垮发号监控号段消耗速率在段快耗尽前告警杜绝段耗尽 DB 抖动双重打击。如果业务量小、部署简单、机器时钟稳定物理机、有 NTP 防护我仍然推荐雪花——它零依赖、性能好、调试直观。但凡上云、上容器、日增量过千万我建议直接上 Leaf-segment把时钟风险从根上移出 ID 生成链路。补充一句Leaf-segment 的段步长step也别拍脑袋步长太小 DB 访问频繁太大重启丢号多。我们按单节点峰值 QPS × 60 秒估算线上 step 设为 50 万DB 每分钟才被访问一次单节点重启最多浪费 50 万号在日增 2 亿的量级下完全可接受。另外 Leaf 的 DB 段表建议用独立的 biz_tag 区分不同业务线避免一个业务把段用爆影响其他业务的发号。思考题假设你的 workerId 用IP 后 10 位取模 1024生成容器重启后 IP 变了导致 workerId 和上一次重复同时发生时钟回拨。这时雪花算法会分别触发什么问题如果用 Leaf-segment这两类问题还存在吗写在最后雪花、号段、Leaf 不是谁更好是适用面不同。雪花轻量但怕时钟回拨和 workerId 冲突号段/Leaf 用内存发号 DB 批量取段绕开时钟问题代价是多一个服务依赖。我们日增 2 亿的表最终选 Leaf-segment是因为容器化让雪花的风险变得不可控。选 ID 方案先问自己我的部署环境时钟可信吗ID 重复我能承受吗答案决定选型而不是哪个听起来更先进。

相关新闻

HunterPie终极指南:3分钟打造你的《怪物猎人:世界》智能狩猎助手

HunterPie终极指南:3分钟打造你的《怪物猎人:世界》智能狩猎助手

HunterPie终极指南:3分钟打造你的《怪物猎人:世界》智能狩猎助手 【免费下载链接】HunterPie-legacy A complete, modern and clean overlay with Discord Rich Presence integration for Monster Hunter: World. 项目地址: https://gitcode.com/gh_mi…

2026/10/16 17:07:49 阅读更多 →
Arduino传感器扩展板设计:解决多传感器接口、供电与信号调理难题

Arduino传感器扩展板设计:解决多传感器接口、供电与信号调理难题

1. 项目概述:一块“万能钥匙”的诞生 最近在整理工作室的传感器盒子,看着里面堆得满满当当的DFrobot、Seeed Studio以及其他各种品牌的传感器模块,接线、供电、电平匹配这些琐事总是让人头疼。特别是当你想快速验证一个想法,或者带…

2026/10/8 5:33:13 阅读更多 →
3大技术创新解析:ClearerVoice-Studio如何重塑语音处理技术栈

3大技术创新解析:ClearerVoice-Studio如何重塑语音处理技术栈

3大技术创新解析:ClearerVoice-Studio如何重塑语音处理技术栈 【免费下载链接】ClearerVoice-Studio An AI-Powered Speech Processing Toolkit and Open Source SOTA Pretrained Models, Supporting Speech Enhancement, Separation, and Target Speaker Extractio…

2026/10/16 11:39:49 阅读更多 →

最新新闻

C语言——makefile的使用

C语言——makefile的使用

makefile如果有多个.c文件要编译,用gcc编译每次都要重新输入,每次要编很多,如果出错改好又要全部都编一遍,如gcc main.c add.c sub.c来把文件拼接成a.out,这是很浪费时间和编译效率的这时候用makefile可以提升效率使用规则如下写m…

2026/10/17 10:46:08 阅读更多 →
学习算法之双指针篇(1)(c++版)

学习算法之双指针篇(1)(c++版)

一、概念和分类 双指针是一种‌用两个指针协同移动来减少遍历次数、降低时间复杂度的思路。 常见的有对撞指针和快慢指针。 1.对撞指针 • 对撞指针是从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。终⽌条件⼀般是两个…

2026/10/17 10:46:08 阅读更多 →
【高质量毕设开题】 基于微信小程序的智慧校园生活服务平台设计与实现(源码+报告)

【高质量毕设开题】 基于微信小程序的智慧校园生活服务平台设计与实现(源码+报告)

温馨提示: 本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示: 本人主页置顶文章(点我)开头有 CSDN平台官方提供的学长联系方式的名片! 温馨提示: 本人主页置顶文章(点我)开头有 CSDN 平…

2026/10/17 10:46:08 阅读更多 →
C#语音测试例程:用System.Speech打造TTS合成验证工具

C#语音测试例程:用System.Speech打造TTS合成验证工具

简介:面向初学者的C#语音合成测试例程,基于Visual Studio 2010与Windows自带System.Speech组件,演示通过Text-to-Speech技术将文本转换为语音播报的全过程,适合刚接触TTS开发、想了解人机交互或无障碍应用实现的开发者&#xff0c…

2026/10/17 10:46:08 阅读更多 →
坐标转换实战:从坐标系原理到代码实现与避坑指南

坐标转换实战:从坐标系原理到代码实现与避坑指南

1. 坐标转换到底在转什么:从一次地图偏移说起坐标转换这个词,听起来像是测绘专业才需要关心的东西,但实际上只要你碰过地图开发、做过定位功能、处理过轨迹数据,甚至只是想把手机拍的照片标到地图上,就一定会跟它打交道…

2026/10/17 10:46:08 阅读更多 →
ENUM 和 SET:方便但有代价

ENUM 和 SET:方便但有代价

个人主页&#xff1a;> for_ever_love__ <&#xff08;欢迎各位大佬莅临&#x1f60a;&#xff09; 其他栏目: > 大模型开发从0到1 < 其他栏目: > iOS项目总结大全 < 其他栏目: > 我想学python了 < 其他栏目: > iOS UI < 文章目录ENUM 和 SET&am…

2026/10/17 10:45:08 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介&#xff1a;《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南&#xff0c;重点围绕V3与R1两款模型的能力差异与适用场景展开&#xff0c;帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件&#xff0c;压缩包大小约311KB&#xff0c;篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介&#xff1a;这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档&#xff0c;共收录两百余条可直接套用的润色指令&#xff0c;涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度&#xff0c;并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介&#xff1a;本资源是一份基于OpenCV的本科课程设计完整报告&#xff0c;面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生&#xff0c;解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介&#xff1a;基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码&#xff0c;面向计算机相关专业课程设计与期末大作业学生&#xff0c;以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程&#xff0c;…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程&#xff1a;键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化&#xff0c;十个新手有八个栽在"往输入框里填东西"这件事上&#xff1a;要么填不进去&#xff0c;要么填了一半&#xff0c;要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程&#xff1a;阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀&#xff1a;什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面&#xff0c;跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →