
分布式 ID 完整选型 落地方案生产可用分场景直接选一、主流 5 种方案优缺点总览1. UUID / UUID36优点本地生成无 RPC、简单缺点无序、太长 36 位、数据库索引差、泄露 MAC 地址旧版适用简单日志、临时业务 ID严禁做主键2. 数据库自增主键单库优点连续有序、稳定、最简单缺点单点瓶颈分库分表完全不能用适用单体小型系统3. 数据库号段模式Leaf、百度 UidGenerator原理业务一次性批量拿一段 ID 缓存本地用完再去 DB 申请下一号段优点有序、性能极高、稳定、可持久化、故障不重复缺点需要单独一张号段表轻微开发量适用绝大多数企业业务、订单、财务、主业务主键最稳妥通用选型4. Redis INCR / 号段优点性能爆炸原子自增缺点宕机丢未持久化增量、依赖 Redis 集群稳定性适用高并发秒杀、临时流水号5. Snowflake 雪花算法Twitter64bit 结构时间戳 (41) 机器 ID (10) 序列号 (12)优点全局有序、纯本地生成、不依赖第三方、64 位 long 型缺点时钟回拨致命问题、机器 ID 要手动分配维护适用高并发互联网业务、海量数据场景必须解决时钟回拨6. 美团 Leaf同时支持雪花 号段企业级成熟开源框架直接拿来用业界落地最多。二、明确选型决策表直接对照业务选场景 1传统 ERP/MES/ 财务 / 支付、要求绝对不重复、数据稳妥✅优先Leaf 号段模式强一致性、无时钟问题、运维简单、数据库可溯源分库分表天然支持场景 2超高并发、百万 QPS、海量流水、不在乎短暂时钟风险✅Snowflake自研 / Leaf 雪花模式必须配套时钟监控、回拨拒绝策略、机器 ID 注册中心场景 3简单临时 ID、内部埋点、非主键✅ UUID / 简单雪花场景 4秒杀短时效流水号✅ Redis 自增三、雪花算法详细落地关键点避坑核心64 位标准结构1bit 符号位 | 41bit 毫秒时间戳 | 10bit 机器 ID | 12bit 序列号机器 ID 10bit最多 1024 台机器序列号 12bit每毫秒单节点最多 4096 个 ID三大致命坑 解决方案时钟回拨最严重现象服务器 NTP 同步回拨生成重复 ID解决本机记录上一次生成时间当前时间 上次时间 → 直接抛出异常 / 自旋等待Leaf 雪花自带时间回拨补偿禁止虚拟机时间漂移机器 ID 重复方案启动注册到 Zookeeper/Redis自动分配唯一机器号物理机用 IP 最后 10 位、容器用 K8s pod 固定标识单机毫秒内 4096 序列号耗尽自旋等到下一毫秒即可四、号段模式落地架构强烈推荐制造业 / 企业系统1. 单独号段数据表CREATE TABLE id_segment ( business_tag VARCHAR(50) PRIMARY KEY COMMENT 业务标识:order,receivable, max_id BIGINT NOT NULL COMMENT 当前已分配最大号, step INT NOT NULL COMMENT 每次申请步长, update_time DATETIME );示例初始化一条ordermax_id10000step10002. 运行逻辑服务启动拉取一段10001~11000 缓存在内存业务直接内存取号无 DB 请求内存剩余 10% 阈值异步去 DB 抢占下一段号段CAS 更新 max_id数据库只有简单 UPDATEupdate id_segment set max_id max_id step where business_tag ?原子更新绝对不会重复3. 优势DB 宕机内存号段还能继续支撑很久完全没有时钟问题金融财务零风险有序数字 IDMySQL 主键索引性能拉满五、开源成熟框架直接落地不用重复造轮子美团 Leaf首选两种模式自由切换Segment 号段 Snowflake 支持监控、号段自动扩容、雪花时钟回拨处理、RPC 接入简单百度 UidGenerator基于雪花借助 Redis 解决机器 ID 与时钟滴滴 TinyId纯号段方案轻量六、Java 生产极简落地建议企业内部管理系统、MES、WMS、财务单据采用Leaf Segment 号段高并发 C 端业务、千万级订单采用Leaf Snowflake 模式开启时钟回拨保护禁止自己手写简易雪花时钟回拨一定会踩坑七、分库分表配套规则雪花 ID取 ID 取模分片号段 ID同样取模全局连续完全兼容分片三套完整可直接投产代码自研号段工具类 安全雪花算法 SpringBoot 接入美团 Leaf一、自研数据库号段分布式 ID企业 ERP/MES/ 财务最稳1、号段数据表CREATE TABLE id_segment ( business_tag varchar(50) NOT NULL COMMENT 业务标识order、receivable、work_order, max_id bigint NOT NULL DEFAULT 0 COMMENT 已分配最大ID, step int NOT NULL DEFAULT 1000 COMMENT 每次申请号段长度, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (business_tag) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT分布式号段表; -- 初始化测试数据 INSERT INTO id_segment(business_tag,max_id,step) VALUES (order,10000,2000);2、MyBatis MapperMapper public interface IdSegmentMapper { Update(UPDATE id_segment SET max_id max_id step WHERE business_tag #{tag}) int increaseSegment(Param(tag) String tag); Select(SELECT max_id,step FROM id_segment WHERE business_tag #{tag}) IdSegment selectByTag(Param(tag) String tag); } Data public class IdSegment { private Long maxId; private Integer step; }3、号段生成器核心工具类import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.Map; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; Component public class SegmentIdGenerator { Resource private IdSegmentMapper idSegmentMapper; // 本地缓存号段 private final MapString, SegmentBuffer bufferMap new ConcurrentHashMap(); // 异步预加载线程池 private final ExecutorService pool Executors.newSingleThreadExecutor(); private static class SegmentBuffer { // 当前号段起始、截止、当前取值 AtomicLong current; long end; int step; volatile boolean isLoading; } public Long nextId(String businessTag) { SegmentBuffer buffer bufferMap.computeIfAbsent(businessTag, k - initBuffer(k)); long id buffer.current.getAndIncrement(); // 剩余10%阈值异步拉取下一号段 if (id buffer.end - buffer.step * 0.1 !buffer.isLoading) { loadNextAsync(buffer, businessTag); } if (id buffer.end) { synchronized (buffer) { while (id buffer.end) { try { buffer.wait(); } catch (InterruptedException e) { throw new RuntimeException(获取号段超时); } } id buffer.current.getAndIncrement(); } } return id; } private SegmentBuffer initBuffer(String tag) { IdSegment seg idSegmentMapper.selectByTag(tag); SegmentBuffer buf new SegmentBuffer(); buf.step seg.getStep(); buf.end seg.getMaxId(); buf.current new AtomicLong(buf.end - buf.step 1); return buf; } private void loadNextAsync(SegmentBuffer buffer, String tag) { if (!buffer.isLoading) { buffer.isLoading true; pool.submit(() - { try { // CAS原子递增 idSegmentMapper.increaseSegment(tag); IdSegment newSeg idSegmentMapper.selectByTag(tag); synchronized (buffer) { buffer.end newSeg.getMaxId(); buffer.current.set(buffer.end - buffer.step 1); buffer.notifyAll(); } } finally { buffer.isLoading false; } }); } } }使用Autowired private SegmentIdGenerator segmentIdGenerator; Long orderId segmentIdGenerator.nextId(order);二、自带时钟回拨防护的雪花算法可直接上线public class SafeSnowflakeIdGenerator { // 64位划分 private static final long TIME_BIT 41L; private static final long WORKER_BIT 10L; private static final long SEQ_BIT 12L; private static final long MAX_WORKER_ID (1L WORKER_BIT) - 1; private static final long MAX_SEQ (1L SEQ_BIT) - 1; private static final long WORKER_SHIFT SEQ_BIT; private static final long TIME_SHIFT SEQ_BIT WORKER_BIT; // 基准时间2025-01-01 private static final long EPOCH 1735689600000L; private final long workerId; private long lastTime 0L; private long sequence 0L; public SafeSnowflakeIdGenerator(long workerId) { if (workerId 0 || workerId MAX_WORKER_ID) { throw new IllegalArgumentException(机器ID超出0~1023); } this.workerId workerId; } public synchronized long nextId() { long now System.currentTimeMillis(); // 时钟回拨 if (now lastTime) { long offset lastTime - now; // 回拨小于50ms自旋等待超过直接抛异常拒绝生成 if (offset 50) { try { Thread.sleep(offset); now System.currentTimeMillis(); } catch (InterruptedException e) { throw new RuntimeException(时钟回拨生成ID失败); } } else { throw new RuntimeException(严重时钟回拨禁止生成ID); } } // 同一毫秒自增序列号 if (now lastTime) { sequence (sequence 1) MAX_SEQ; if (sequence 0) { // 序列号用尽等到下一毫秒 while (now lastTime) { now System.currentTimeMillis(); } } } else { sequence 0; } lastTime now; return ((now - EPOCH) TIME_SHIFT) | (workerId WORKER_SHIFT) | sequence; } }机器 ID 分配方案物理机IP最后两段取模1024K8s 容器注册到 Redis/ZK 自动分配唯一 workerId三、SpringBoot 接入美团 Leaf 完整配置生产首选1、架构选择同时部署Segment 号段做主业务 IDSnowflake 做高并发流水 ID2、application.ymlleaf: segment: enable: true url: jdbc:mysql://localhost:3306/leaf_db?useUnicodetrue username: root password: xxx snowflake: enable: true # 使用ZK自动分配workerId杜绝重复 zk-address: 127.0.0.1:21813、依赖 Mavendependency groupIdcom.sankuai.inf.leaf/groupId artifactIdleaf-core/artifactId version1.0.4/version /dependency4、注入使用Autowired private SegmentIDGenImpl segmentIDGen; Autowired private SnowflakeIDGenImpl snowflakeIDGen; // 号段ID订单、财务单据 Long orderId segmentIDGen.get(order).getId(); // 雪花ID高并发流水 Long flowId snowflakeIDGen.get(flow).getId();四、三种方案选型极简总结MES/WMS/ 财务 / ERP 单据、要求绝对稳定无时钟风险→ 自研号段 / Leaf Segment高并发千万级 C 端订单、海量数据→ 安全雪花 / Leaf 雪花小型单体项目→ 简单雪花即可