前言为什么单机自增 ID 在分布式环境彻底失效在单体项目中我们直接使用数据库自增主键就能保证每条数据唯一。一旦数据量成万、上百万甚至上亿系统就会发展到分库分表、微服务集群架构那么单机自增方案会出现致命缺陷分表主键冲突多个分表独立自增都会生成 1、2、3…… 相同 ID无法做分片路由多服务 ID 重复多实例同时插入数据库自增起点一致主键重复报错数据无法全局唯一标识订单、用户、流水需要全局唯一编号做索引、分区分片、业务追踪。因此我们需要分布式 ID在多机器、多数据库环境下生成全局不重复、满足高并发、有序友好的数字唯一标识。行业主流分为两大技术路线中心化号段模式TinyID、Leaf-segment依赖 Redis/DB 做号段分发大公司主流本地生成算法雪花算法 Snowflake纯内存计算无中心化依赖但存在时钟缺陷小公司主流一、中心化号段分布式IDTinyID/Leaf核心原理1.核心设计思想不每次请求都去 Redis/DB 拿单个 ID而是批量预取一段 ID 缓存到本地内存绝大多数 ID 请求直接走内存大幅降低中间件压力当本地 ID 池消耗到阈值时异步去拉取下一批号段。 中间件可选Redis、MySQL2.完整流程2.1核心配置参数// 全局开关开启批量号段 batch: true // 单次从Redis拉取ID数量 batchSize: 10 // 阈值比例本地ID剩余不足50%时触发预拉取 percent: 0.5 // 本地缓存容器线程安全Mapkey业务标识(order/user)value当前可用最大ID ConcurrentHashMapString, Long idMap2.2分布逻辑1第一次获取ID本地无缓存判断idMap定义的一个concurrentHashMap来充当本地缓存容器是线程安全的中当前业务 key 值为 0代表未初始化调用 Redis 原子命令INCRBY key batchSize一次性累加 batchSize Redis 原子操作保证多服务并发申请号段不会重叠全局号段连续例如INCRBY order 10Redis 返回 10则本次拿到号段1~10将最大可用 ID9存入本地 Map返回第一个 ID 1。(因为要返回一个ID因此在代码里面要写increment-1)2正常获取ID本地有缓存未触达阈值取出 Map 中剩余最大 ID自减 1 后回填 Map 并返回例如本地缓存剩余 5返回 5Map 更新为 4。3触发阈值自动预拉新号段剩余 ID ≤ batchSize * percent10*0.55时同步向 Redis 申请新一批号段Redis 再次 INCRBY 拿到下一段 ID新旧号段合并更新本地缓存最大值继续对外分发 ID。4关闭批量模式如果设置的batchfalse就不是批量申请ID就是每次获取都需要执行INCR key1单次Redis请求高并发时性能差仅低流量业务时使用2.3线程安全保障为什么需要线程安全保障因为可能会有多个服务器来申请批量ID字段所以本地会有一个数据库未来的值多个服务器的申请总ID数如果不是线程安全的那就会出现多个服务器申请重叠总ID数不准确ConcurrentHashMap保证多线程并发读写本地 ID 池获取、更新本地 ID 段逻辑加synchronized同步锁防止多线程同时触发 Redis 拉取重复申请号段RedisINCRBY是单线程原子命令分布式多服务并发申请不会出现号段重叠、漏号、重复。3.完整流程客户端请求ID ↓ 查询本地ConcurrentHashMap中业务ID剩余值 ↓ ├─本地无缓存 → Redis批量自增获取一批ID存入本地返回首个ID ├─本地有缓存 ├─剩余ID低于阈值 → 同步拉取新号段合并缓存后自减返回ID └─剩余ID充足 → 本地ID自减直接返回4.优缺点分析优点超高并发性能90% 请求走本地内存Redis 请求极少单机 QPS 轻松 10 万 ID 严格连续递增Redis 自增保证全局 ID 有序数据库 B 树索引友好无时钟依赖完全不依赖服务器系统时间不存在雪花算法的时钟回拨风险业务隔离不同业务订单、用户使用不同 Redis key号段互不干扰。缺点强依赖 Redis 可用性Redis 宕机后无法拉取新号段本地 ID 池消耗完毕后系统不可用内存占用每个业务单独缓存一段 ID多业务场景内存开销小幅上升号段浪费服务重启时本地未用完的 ID 会永久丢弃出现断号业务可接受。5.工业级优化Leaf双Buffer机制美团TinyID 基础版仅单缓存号段耗尽拉取 Redis 时会阻塞请求Leaf 新增双缓冲 ID 池当前号段剩余达到阈值异步线程预加载第二个备用号段当前号段耗尽直接无缝切换备用池请求无阻塞消除性能尖刺。二、雪花算法Snowflake本地内存生成分布式ID雪花算法是本地util无需中间件但依赖毫秒时间戳多服务器同时请求存在重复风险依靠机器ID区分节点同一台机器同一毫秒最多生成4096个ID。1.64位Long结构标准拆分雪花ID是一个64位有符号Long数字结构固定0 | 41位毫秒时间戳 | 10位机器ID | 12位序列号这里解释一下为什么有时间戳了还要有机器ID标识这是因为可能会存在多台服务器那么在相同时间甚至是毫秒级别会有多台机器同时生成ID那么这就会造成ID 重复因此在添加机器ID这里是基于MAC地址来区分的因为每个机器的MAC地址都是独一无二的1 位符号位固定 0保证 ID 为正数不使用负数41 位时间戳自定义起始纪元到当前的毫秒数可使用约 69 年10 位机器 IDWorkerID区分集群不同服务器最多支持 1024 台机器12 位序列号Sequence同一台机器同一毫秒内自增最大值 4095单毫秒支持 4096 个 ID。2.生成逻辑获取当前系统毫秒时间戳对比上一次生成 ID 的时间戳相同毫秒序列号 1序列号溢出则阻塞等待下一毫秒更大毫秒序列号重置为 0四段数据按位拼接得到全局唯一 64 位 ID。3.两大致命缺陷3.1始终回拨ID重复服务器 NTP 时间同步、手动调时会导致系统时间回退 例上一条 ID 时间戳2026-06-28 15:00:10.500系统回拨到15:00:10.400 此时生成的时间戳小于历史拼接相同机器 ID、序列号直接生成重复 ID数据库主键冲突。行业通用解决方案小幅回拨5ms自旋 sleep 等待时间追上历史大幅回拨5ms直接抛出异常拒绝生成人工介入Leaf 增强方案ZK 持久化存储节点历史最大时间戳启动校验时钟。3.2机器 ID 分配难题10 位 WorkerID 必须保证集群内全局唯一否则多台机器机器 ID 重复同一毫秒会产生相同 ID静态配置每台机器硬编码 ID扩容、容器弹性伸缩维护成本极高动态分配依赖 ZK/Redis 启动时申请唯一机器 ID引入第三方组件失去 “无中间件” 优势K8s 容器Pod 动态 IP 无法固定难以通过 MAC/IP 哈希稳定生成机器 ID。4.雪花算法的优缺点优点完全本地计算无需 Redis、DB 等中间件网络开销为 0单机并发性能极高64 位 Long 类型占用存储空间小MySQL 主键存储高效时间趋势有序同一机器 ID 随时间递增适合按时间分片、分表路由。缺点时钟回拨无解无法从底层彻底规避只能事后容错集群内 ID 不完全连续不同机器同一毫秒生成的 ID 大小混乱不适合严格连续号业务节点上限固定10 位机器 ID 仅支持 1024 台服务大规模集群需修改位分配容器云环境机器 ID 分配复杂弹性扩缩容极易出错。三、中心化号段模式 vs 雪花算法对比维度TinyID/Leaf 号段模式Redis雪花算法 Snowflake生成位置本地内存缓存依赖 Redis 拉取号段纯本地内存无网络 IO全局有序性严格连续递增仅单机时间有序集群乱序外部依赖必须 Redis/DB无依赖机器 ID 分配除外并发上限极高仅少量请求访问 Redis单机百万级 QPS时钟风险完全不存在时钟回拨会造成 ID 重复扩容难度服务无限扩容Redis 横向分片受机器 ID 位数限制断号情况服务重启丢弃未使用号段存在断号无断号生成即有效适用场景订单、支付、分库分表主键、要求连续 ID日志 ID、短链、中小规模微服务四、业务场景选择场景 1电商订单、支付流水、分库分表推荐号段模式要求 ID 连续、分片路由稳定、不能接受 ID 重复风险系统集群规模大已有 Redis 集群优先 TinyID/Leaf-segment。场景 2小型后台、内部管理系统推荐雪花算法服务实例少几十台以内无高并发峰值不愿部署额外中间件手动固定机器 ID 即可规避大部分问题。场景 3云原生 K8s 弹性容器推荐 Leaf 号段容器动态创建销毁雪花机器 ID 难以稳定分配号段模式无需感知节点信息扩容零改造。场景 4千万级大促超高并发双方案混合主业务订单使用 Redis 号段保证稳定有序日志、埋点使用雪花算法分流减轻 Redis 压力。五、常见的疑惑1.开篇就说道到的分库分表为什么这种情况不能用数据库自增单库自增只在当前表唯一多库多张表都会从 1 开始自增分片时导致多个数据库表的 ID 重复仅保证了表内的ID唯一但是不保证全局唯一而分布式 ID 全局唯一可直接通过 ID 哈希 / 取模完成分片路由。2.Redis号段模式为什么要用INCRBY批量自增不用单次INCR单次 INCR 每生成一个 ID 访问一次 Redis高并发下网络 IO 阻塞批量拉取一段缓存本地大幅降低 Redis 压力QPS 提升数十倍。