极高 进阶
分布式ID生成方案#
一句话答案#
常用方案:雪花算法(有序高性能)、号段模式(预分配+高可用)、Redis INCR(简单有序)、UUID(无序不推荐做主键)。
核心要点
为什么需要分布式 ID? 在分库分表或微服务架构下,数据库自增 ID 无法保证全局唯一,需要一种全局唯一的 ID 生成方案。
常见方案对比:
| 方案 | 优点 | 缺点 |
|---|---|---|
| UUID | 本地生成,无网络开销,全球唯一 | 无序(B+ 树插入性能差)、太长(128 位字符串)、不适合做主键 |
| 数据库自增 | 简单、有序 | 单点瓶颈、分库后不唯一、性能受限于 DB |
| 数据库号段模式 | 批量获取减少 DB 访问 | 仍依赖 DB、号段用完需重新申请 |
| Redis INCR | 性能高、有序 | 依赖 Redis 可用性、持久化问题 |
| Snowflake 雪花算法 | 本地生成、有序、高性能 | 依赖机器时钟、时钟回拨问题 |
Snowflake 雪花算法原理(64 位 Long 型 ID):
0 | 0000000 00000000 00000000 00000000 00000000 0 | 00000 00000 | 000000000000
│ │ │ │
│ │ 41 位时间戳 │ 10 位机器ID │ 12 位序列号
│ │ (毫秒级,可用约 69 年) │ │
│ │ │ │
│ 1 位符号位(固定为 0,表示正数) │ │
│ 5位数据中心 + 5位机器
│ (最多 1024 个节点)
│
最高位(始终为 0)
各部分的含义:
1 位:符号位,固定 0
41 位:时间戳(当前时间 - 起始时间的毫秒差),可用约 69 年
10 位:工作机器 ID(5 位 datacenterId + 5 位 workerId),最多 1024 台机器
12 位:同一毫秒内的序列号,最多 4096 个/毫秒
单机理论 QPS:4096 * 1000 = 409.6 万/秒plaintextSnowflake 的时钟回拨问题:
问题:服务器时钟被回调(NTP 同步、手动修改),导致生成重复 ID
解决方案:
① 直接抛异常(简单粗暴)
if (currentTimestamp < lastTimestamp) throw new RuntimeException("时钟回拨");
② 等待时钟追上(适合小幅回拨,如几毫秒)
while (currentTimestamp < lastTimestamp) {
currentTimestamp = System.currentTimeMillis();
}
③ 使用扩展位(预留回拨序号位)plaintext业界改进方案:
| 方案 | 团队 | 改进点 |
|---|---|---|
| Leaf(美团) | 美团点评 | 号段模式 + Snowflake 双模式,号段模式预加载双 buffer |
| uid-generator(百度) | 百度 | 借鉴 Snowflake,使用 RingBuffer 预生成 ID 缓存,解决时钟回拨 |
| Tinyid(滴滴) | 滴滴 | 号段模式 + 多 DB 支持 |
面试回答(2分钟版)
分布式ID的核心需求是全局唯一、趋势递增、高性能。主流方案有四种。UUID本地生成无网络开销,但128位字符串太长且无序,作为数据库主键会导致B+树频繁页分裂,性能很差,一般不推荐。数据库自增ID简单有序但有单点瓶颈,分库后无法保证全局唯一。Redis INCR性能高且有序,但增加了对Redis的依赖和持久化风险。最推荐的是Snowflake雪花算法,64位Long型ID由1位符号位、41位毫秒级时间戳、10位机器ID和12位序列号组成,本地生成不依赖外部服务,单机理论QPS达到409.6万每秒,ID趋势递增对B+树索引友好。雪花算法的主要问题是时钟回拨,如果NTP同步导致时钟倒退会生成重复ID,常见解决方案是小幅回拨时等待追上,大幅回拨直接抛异常。业界改进方案有美团Leaf支持号段模式和Snowflake双模式,百度uid-generator用RingBuffer预生成ID解决时钟回拨问题。
追问与易错
追问方向:
- “你们用的哪种方案?”→ 结合项目讲,说清选型理由(如用雪花算法因为不依赖外部服务且有序对 B+ 树友好,或用 Leaf 号段模式因为需要数据库友好的连续 ID)
- “Leaf 和 UidGenerator 了解吗?”→ Leaf 是美团开源方案支持号段+雪花双模式,号段模式双 buffer 预加载避免等待;UidGenerator 是百度方案用 RingBuffer 预生成 ID 解决时钟回拨
- “需要全局递增还是趋势递增?”→ 大部分场景趋势递增即可(对 B+ 树索引友好),全局严格递增需要中心化生成(如 Redis INCR)有性能瓶颈,按需选择
易错点:
- ❌ UUID 也能做分布式 ID——无序不适合做主键
- ❌ 雪花算法不会出错——时钟回拨必须处理