目录

常用的分布式ID设计方案

常用的分布式ID设计方案

分布式系统中生成唯一ID是常见需求,尤其在微服务、分库分表等场景下,需要满足 全局唯一、有序递增、高可用、低延迟 等特性。以下是七种主流方案及其核心逻辑:


一、数据库自增ID

原理 :利用数据库自增主键(如MySQL的 AUTO_INCREMENT )生成连续ID。

优化方案

  • 单数据库多实例 :不同实例设置不同自增步长( auto_increment_incrementauto_increment_offset )。

  • 分片号段模式 :每次从数据库批量获取ID段(如1~1000),应用内存中分配。

    优点 :简单易用、ID连续递增。

    缺点 :依赖数据库性能,扩展性差,存在单点故障风险。

    适用场景 :小型系统、单数据库架构。


二、UUID

原理 :基于时间、MAC地址、随机数生成128位唯一字符串(如 550e8400-e29b-41d4-a716-446655440000 )。

优点 :本地生成、无网络开销、无重复风险。

缺点

  • 无序性导致数据库索引效率低(B+树分裂频繁)。

  • 长度过长(36字符),存储和传输效率低。

    适用场景 :临时标识、低并发且无需排序的场景。


三、Redis生成ID

原理 :利用Redis单线程特性,通过 INCRINCRBY 命令生成递增ID。

优化方案

  • 集群分片 :每个Redis实例分配独立步长(类似数据库多实例)。

  • Lua脚本原子操作 :批量获取ID段,减少网络开销。

    优点 :性能高于数据库(10万+ QPS),可扩展性强。

    缺点 :需维护Redis集群,持久化策略影响数据可靠性。

    适用场景 :高并发但对ID连续性要求不高的场景。


四、Snowflake算法(雪花算法)

原理 :由Twitter提出的64位ID结构,包含时间戳、机器ID、序列号:

0 | 41位时间戳 | 10位机器ID | 12位序列号
  • 时间戳 :毫秒级时间差(自定义起始时间,如2020-01-01)。

  • 机器ID :通过ZooKeeper、配置中心或IP哈希分配。

  • 序列号 :同一毫秒内的并发序列(支持4096/ms)。

    优点 :本地生成、性能高(单机万级QPS)、ID有序。

    缺点 :依赖时钟同步(时钟回拨导致重复ID)。

    解决方案

  • 关闭NTP同步(不推荐)。

  • 记录上次生成时间戳,检测到回拨时等待或报警。

    适用场景 :分布式系统、分库分表场景。


五、Leaf算法

原理 :美团开源的分布式ID生成服务,整合 Snowflake号段模式

两种模式

  1. Leaf-Snowflake :优化时钟回拨问题,通过ZooKeeper协调Worker ID。

  2. Leaf-Segment :基于数据库号段预分配,双Buffer异步更新。

    优点 :高可用、支持监控和管理后台。

    缺点 :需独立部署服务,增加系统复杂度。

    适用场景 :中大型企业级应用。


六、TinyID(百度)

原理 :基于数据库号段模式,支持HTTP和RPC两种接入方式。

核心优化

  • 多号段缓存 :提前加载多个号段,避免分配延迟。

  • 动态步长调整 :根据历史QPS自动调整号段大小。

    优点 :高吞吐、低延迟。

    缺点 :依赖数据库和中心化服务。

    适用场景 :电商、金融等高并发业务。


七、MongoDB ObjectId

原理 :12字节(24字符)的十六进制字符串,结构如下:

4字节时间戳 | 5字节机器ID | 3字节进程ID | 4字节计数器

优点 :无需中心化服务,内置分布式支持。

缺点 :无序性、长度较长。

适用场景 :MongoDB数据库集成场景。


方案对比与选型建议

方案有序性性能复杂度适用场景
数据库自增ID✔️单数据库、小规模系统
UUID临时标识、低并发场景
Redis✔️高并发、非强连续场景
Snowflake✔️极高分布式系统、分库分表
Leaf✔️企业级应用、需高可用
TinyID✔️高并发、动态调整需求
MongoDB ObjectIdMongoDB集成场景

选型原则

  1. 业务需求 :是否需要有序性(影响数据库索引效率)?
  2. 并发量级 :是否需支持每秒数万级ID生成?
  3. 系统架构 :是否已存在ZooKeeper、Redis等中间件?
  4. 容灾能力 :能否接受单点故障或时钟回拨风险?

实战示例:Snowflake的Java实现

public class SnowflakeIdGenerator {
    private final long twepoch = 1625097600000L; // 2021-07-01 00:00:00
    private final long workerIdBits = 5L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long sequenceBits = 12L;
    private final long workerIdShift = sequenceBits;
    private final long timestampShift = sequenceBits + workerIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long workerId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(long workerId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException("Worker ID超出范围");
        }
        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("时钟回拨,拒绝生成ID");
        }
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - twepoch) << timestampShift)
                | (workerId << workerIdShift)
                | sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}

总结

分布式ID设计需权衡 性能、有序性、可用性和复杂度 。Snowflake及其衍生方案(如Leaf)是通用性最强的选择,而号段模式适合数据库友好的场景。最终选型应基于业务实际需求,结合团队技术栈和运维能力,确保系统的稳定性和扩展性。