常用的分布式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 ObjectId MongoDB集成场景

选型原则

  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)是通用性最强的选择,而号段模式适合数据库友好的场景。最终选型应基于业务实际需求,结合团队技术栈和运维能力,确保系统的稳定性和扩展性。

相关推荐
Unity官方开发者社区34 分钟前
《Cryptical Path》开发诀窍:像玩游戏一样开发一款类Rogue游戏
java·游戏·玩游戏
_星辰大海乀37 分钟前
表的设计、聚合函数
java·数据结构·数据库·sql·mysql·数据库开发
IT成长史1 小时前
deepseek梳理java高级开发工程师微服务面试题-进阶版
java·spring cloud·微服务
紫璨月1 小时前
Feign异步模式丢失上下文问题
spring cloud·openfeign
zkmall1 小时前
Java + 鸿蒙双引擎:ZKmall开源商城如何定义下一代B2C商城技术标准?
java·开源·harmonyos
陌路物是人非1 小时前
uniapp取消浏览自动填充
java·服务器·uni-app
獨枭1 小时前
使用 163 邮箱实现 Spring Boot 邮箱验证码登录
java·spring boot·后端
伍六星1 小时前
maven和npm区别是什么
java·npm·maven
才知山高路远1 小时前
Java - Junit框架
java·junit·log4j
维基框架1 小时前
Spring Boot 封装 MinIO 工具
java·spring boot·后端