面试级「分布式 ID 设计方案」

从目标 → 主流方案 → 对比 → 实战选型 → 源码级思想 ,适合 P6~P7 / 架构岗,可以直接背,也可以当系统设计题来答。


一、分布式 ID 的设计目标(面试开场)

分布式 ID 是跨服务、跨库、跨机器的唯一标识,设计时要满足:

✅ 全局唯一(最基本)

✅ 趋势递增(MySQL B+Tree 友好)

✅ 高性能(高并发下不成为瓶颈)

✅ 高可用(不能依赖单点)

✅ 易接入(业务无感)

✅ 可解析(排查问题方便)


二、常见分布式 ID 方案总览

方案 唯一性 递增 性能 复杂度 推荐指数
UUID ✅ ❌ ✅ ⭐ ⭐⭐
数据库自增 ✅ ✅ ❌ ⭐ ⭐⭐
数据库号段 ✅ ✅ ✅ ⭐⭐ ⭐⭐⭐⭐
Redis 自增 ✅ ✅ ✅ ⭐⭐ ⭐⭐⭐
Snowflake​ ✅ ✅ ✅✅ ⭐⭐⭐ ⭐⭐⭐⭐⭐
Leaf / TinyID ✅ ✅ ✅✅ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

三、主流方案详解(重点)


1️⃣ UUID(不推荐)

复制代码
UUID.randomUUID().toString();

优点

✅ 简单

✅ 无网络开销

✅ 本地生成

缺点(致命)

❌ 无序​ → MySQL 页分裂

❌ 太长(36 位字符串)

❌ 无业务含义

❌ 不易排查问题

👉 结论:只适合临时标识,不适合业务 ID


2️⃣ 数据库自增 ID(单机 OK)

复制代码
CREATE TABLE order (
  id BIGINT AUTO_INCREMENT PRIMARY KEY
);

优点

✅ 简单

✅ 严格递增

缺点

❌ 单点

❌ 性能瓶颈

❌ 分库分表困难

👉 结论:单体系统可用,分布式不行


3️⃣ 数据库号段模式(推荐)

本质是:批量取号,内存发号

原理

复制代码
DB 只存最大值
每次取 1000 个号
缓存在内存中
用完再取

表结构

复制代码
CREATE TABLE id_segment (
  biz_tag VARCHAR(32) PRIMARY KEY,
  max_id BIGINT,
  step INT
);

发号逻辑

复制代码
update id_segment
set max_id = max_id + step
where biz_tag = 'order';

优点

✅ 高性能

✅ 趋势递增

✅ 弱依赖 DB

缺点

❌ 依赖 DB

❌ 重启可能浪费 ID

✅ 中小规模系统非常合适


4️⃣ Redis 自增(简单但有限)

复制代码
INCR order:id

优点

✅ 快

✅ 简单

✅ 原子性

缺点

❌ Redis 宕机风险

❌ 重启可能重复(需持久化)

❌ 无时间戳信息

👉 适合非核心业务


5️⃣ Snowflake(最主流 ⭐⭐⭐⭐⭐)

Twitter 开源,工业级方案

ID 结构(64 bit)

复制代码
| 1 bit | 41 bit 时间戳 | 10 bit 机器ID | 12 bit 序列号 |

0 | 0000000000000000000000000000000000000 | 0000000000 | 000000000000

各部分含义

位段 长度 说明
符号位 1 固定 0
时间戳 41 毫秒级
机器 ID 10 数据中心 + 工作节点
序列号 12 同一毫秒内自增

特点

✅ 趋势递增​

✅ 本地生成​

✅ 高性能​

✅ 无依赖

问题点(面试必问)

问题 解决方案
时钟回拨 等待 / 抛异常 / 用上次时间
机器 ID 分配 Zookeeper / 配置中心
序列号耗尽 等到下一毫秒

Snowflake 代码示例(精简版)

复制代码
public class SnowflakeIdGenerator {

    private final long workerId;
    private final long epoch = 1700000000000L;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(long workerId) {
        this.workerId = workerId;
    }

    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("时钟回拨");
        }

        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & 4095;
            if (sequence == 0) {
                timestamp = waitNextMillis(timestamp);
            }
        } else {
            sequence = 0;
        }

        lastTimestamp = timestamp;

        return ((timestamp - epoch) << 22)
                | (workerId << 12)
                | sequence;
    }

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

6️⃣ 号段模式增强版:Leaf / TinyID(大厂方案)

核心思想

DB + 缓存 + 双 Buffer

  • 一个号段快用完时
  • 异步加载下一个号段
  • 无阻塞切换

✅ 美团 Leaf、滴滴 TinyID 都是这个思路

👉 适合高并发、核心业务


四、方案选型建议(面试总结用)

场景 推荐方案
单体系统 数据库自增
中小规模 数据库号段
高并发核心 Snowflake
大厂级 Leaf / TinyID
非核心业务 Redis / UUID

五、面试 5 分钟口述版(直接背)

面试官:如何设计分布式 ID?

参考回答:

分布式 ID 的核心目标是全局唯一、趋势递增、高性能和高可用。常见方案有 UUID、数据库自增、数据库号段、Redis 自增和 Snowflake。
UUID 虽然简单,但无序且太长,不适合作为数据库主键;数据库自增在分库分表场景下会成为瓶颈;数据库号段模式通过批量取号、内存发号,性能和可用性都不错,适合中小规模系统;Redis 自增依赖 Redis 的可用性,适合非核心业务。
目前最主流的是 Snowflake 算法,它利用时间戳、机器 ID 和序列号生成 64 位 Long 型 ID,趋势递增、本地生成、性能非常高,是 Twitter 开源的工业级方案。实际使用时会遇到时钟回拨问题,可以通过等待或异常方式处理,机器 ID 可以通过 Zookeeper 或配置中心分配。
在大规模场景下,像美团 Leaf、滴滴 TinyID 这样的号段增强方案,通过双 Buffer 预加载,进一步提升了稳定性和性能。
实际选型时,我会根据业务规模和一致性要求来决定,核心业务优先 Snowflake 或 Leaf,非核心业务可以考虑 Redis 或号段模式。


六、一句话总结(收尾金句)

"分布式 ID 没有万能方案,核心是在唯一性、性能和可维护性之间做权衡,Snowflake 是目前最成熟、最通用的选择。"


七、面试官常追问 & 标准答法

追问 回答
Snowflake 时钟回拨怎么办? 等待 / 抛异常 / 使用上次时间
机器 ID 怎么分配? Zookeeper / 配置中心 / 启动时分配
为什么 ID 要趋势递增? 减少 MySQL 页分裂
号段模式浪费 ID 怎么办? 可接受,用可维护性换性能
分布式 ID 能包含业务信息吗? 可以,但不建议,ID 应解耦业务
相关推荐
Wang's Blog2 分钟前
Java框架 SpringCloud 快速入门: Feign 的性能优化
java·spring cloud·性能优化
蛋蛋的就会顺顺的16 分钟前
hot100——二叉树
java·数据结构·算法·leetcode·力扣
打工仔折腾 AI32 分钟前
System Prompt 替代 Few-shot:用规则约束大模型输出的省钱实践
java·人工智能·python·spring·langchain·prompt·ai agent 实战
CarIise39 分钟前
Java实训阶段查漏补缺复习笔记1
java·开发语言·笔记
vx-程序开发2 小时前
【计算机毕设】51300童装超市管理系统的设计与实现
java·vue.js·spring boot·源码·课程设计·毕设·大作业
ym hyd 1112 小时前
题库建立与考试系统源码 Java+SpringBoot+Vue3 前后分离
java·开发语言·vue.js·spring boot·毕设
risc1234562 小时前
什么是码点?
java
JavaPub-rodert2 小时前
公共 Docker 镜像源总失效?用 Harbor Proxy Cache 自建 Docker Hub 镜像缓存
java·缓存·docker
智鸟科技GemeOpen开发者智能设备2 小时前
平安校园 AI 智能实时告警系统 智鸟科技·GemeOpen + 谷华科技·融合创新方案白皮书
java·开发语言·python·物联网·智能家居
自强的小白3 小时前
nacos配置中心面试题(导入配置的优先级),注意是如果后导入相同配置是以 后导入优先。外部入优先和nacos的数据隔离。
java·微服务