分布式 ID

分布式 ID 完整选型 + 落地方案(生产可用,分场景直接选)

一、主流 5 种方案优缺点总览

1. UUID / UUID36

  • 优点:本地生成,无 RPC、简单
  • 缺点:无序、太长 36 位、数据库索引差、泄露 MAC 地址(旧版)
  • 适用:简单日志、临时业务 ID,严禁做主键

2. 数据库自增主键(单库)

  • 优点:连续有序、稳定、最简单
  • 缺点:单点瓶颈,分库分表完全不能用
  • 适用:单体小型系统

3. 数据库号段模式(Leaf、百度 UidGenerator)

原理:业务一次性批量拿一段 ID 缓存本地,用完再去 DB 申请下一号段

  • 优点:有序、性能极高、稳定、可持久化、故障不重复
  • 缺点:需要单独一张号段表,轻微开发量
  • 适用:绝大多数企业业务、订单、财务、主业务主键(最稳妥通用选型

4. Redis INCR / 号段

  • 优点:性能爆炸,原子自增
  • 缺点:宕机丢未持久化增量、依赖 Redis 集群稳定性
  • 适用:高并发秒杀、临时流水号

5. Snowflake 雪花算法(Twitter)

64bit 结构:时间戳 (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

三大致命坑 & 解决方案

  1. 时钟回拨(最严重)
  • 现象:服务器 NTP 同步回拨,生成重复 ID
  • 解决:
    • 本机记录上一次生成时间,当前时间 < 上次时间 → 直接抛出异常 / 自旋等待
    • Leaf 雪花自带时间回拨补偿
    • 禁止虚拟机时间漂移
  1. 机器 ID 重复
  • 方案:
    • 启动注册到 Zookeeper/Redis,自动分配唯一机器号
    • 物理机用 IP 最后 10 位、容器用 K8s pod 固定标识
  1. 单机毫秒内 4096 序列号耗尽
  • 自旋等到下一毫秒即可

四、号段模式落地架构(强烈推荐制造业 / 企业系统)

1. 单独号段数据表

sql 复制代码
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
);

示例初始化一条:order,max_id=10000,step=1000

2. 运行逻辑

  1. 服务启动拉取一段:10001~11000 缓存在内存
  2. 业务直接内存取号,无 DB 请求
  3. 内存剩余 10% 阈值,异步去 DB 抢占下一段号段(CAS 更新 max_id)
  4. 数据库只有简单 UPDATE:update id_segment set max_id = max_id + step where business_tag = ?
  • 原子更新,绝对不会重复

3. 优势

  • DB 宕机,内存号段还能继续支撑很久
  • 完全没有时钟问题,金融财务零风险
  • 有序数字 ID,MySQL 主键索引性能拉满

五、开源成熟框架直接落地(不用重复造轮子)

  1. 美团 Leaf(首选) 两种模式自由切换:Segment 号段 + Snowflake 支持监控、号段自动扩容、雪花时钟回拨处理、RPC 接入简单
  2. 百度 UidGenerator:基于雪花,借助 Redis 解决机器 ID 与时钟
  3. 滴滴 TinyId:纯号段方案,轻量

六、Java 生产极简落地建议

  1. 企业内部管理系统、MES、WMS、财务单据 采用:Leaf Segment 号段
  2. 高并发 C 端业务、千万级订单 采用:Leaf Snowflake 模式,开启时钟回拨保护
  3. 禁止自己手写简易雪花,时钟回拨一定会踩坑

七、分库分表配套规则

  • 雪花 ID:取 ID 取模分片
  • 号段 ID:同样取模,全局连续完全兼容分片

三套完整可直接投产代码:自研号段工具类 + 安全雪花算法 + SpringBoot 接入美团 Leaf

一、自研数据库号段分布式 ID(企业 ERP/MES/ 财务最稳)

1、号段数据表

sql 复制代码
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`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分布式号段表';

-- 初始化测试数据
INSERT INTO id_segment(business_tag,max_id,step) VALUES ('order',10000,2000);

2、MyBatis Mapper

java 复制代码
@Mapper
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、号段生成器核心工具类

java 复制代码
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 Map<String, 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;
                }
            });
        }
    }
}

使用

java 复制代码
@Autowired
private SegmentIdGenerator segmentIdGenerator;
Long orderId = segmentIdGenerator.nextId("order");

二、自带时钟回拨防护的雪花算法(可直接上线)

java 复制代码
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 分配方案

  1. 物理机:IP最后两段取模1024
  2. K8s 容器:注册到 Redis/ZK 自动分配唯一 workerId

三、SpringBoot 接入美团 Leaf 完整配置(生产首选)

1、架构选择

同时部署Segment 号段做主业务 ID,Snowflake 做高并发流水 ID

2、application.yml

XML 复制代码
leaf:
  segment:
    enable: true
    url: jdbc:mysql://localhost:3306/leaf_db?useUnicode=true
    username: root
    password: xxx
  snowflake:
    enable: true
    # 使用ZK自动分配workerId,杜绝重复
    zk-address: 127.0.0.1:2181

3、依赖 Maven

XML 复制代码
<dependency>
    <groupId>com.sankuai.inf.leaf</groupId>
    <artifactId>leaf-core</artifactId>
    <version>1.0.4</version>
</dependency>

4、注入使用

java 复制代码
@Autowired
private SegmentIDGenImpl segmentIDGen;
@Autowired
private SnowflakeIDGenImpl snowflakeIDGen;

// 号段ID(订单、财务单据)
Long orderId = segmentIDGen.get("order").getId();
// 雪花ID(高并发流水)
Long flowId = snowflakeIDGen.get("flow").getId();

四、三种方案选型极简总结

  1. MES/WMS/ 财务 / ERP 单据、要求绝对稳定无时钟风险 → 自研号段 / Leaf Segment
  2. 高并发千万级 C 端订单、海量数据 → 安全雪花 / Leaf 雪花
  3. 小型单体项目 → 简单雪花即可
相关推荐
wWYy.1 小时前
分布式:数据复制
分布式
七夜zippoe8 小时前
深入解析CANN仓库中的HCCL分布式通信库
pytorch·分布式·cann·hccl·通信库
富士康质检员张全蛋8 小时前
Kafka的操作 消费者组 消费位置查看
分布式·kafka
运维行者_8 小时前
如何查看每个IP的带宽使用情况?NetFlow 技术实战指南
开发语言·网络·分布式·后端·架构·带宽
笨鸟先飞的橘猫8 小时前
游戏后端分布式学习——一致性协议Raft
分布式·学习·游戏
谢白羽8 小时前
vllm源码剖析14-vLLM 分布式推理-专家并行EP
笔记·分布式·llm·论文·vllm
霸道流氓气质11 小时前
Kiro 中配置 RabbitMQ MCP Server 指南
分布式·rabbitmq·ruby
国科安芯11 小时前
AS32S601型抗辐射MCU在分布式太空算力架构中的技术演进与应用前景
人工智能·分布式·单片机·嵌入式硬件·架构·边缘计算
不会C语言的菜鸟12 小时前
分布式数据一致性:从工程实践到理论基石
分布式