面试知识库
极高 进阶

分布式ID生成方案#

一句话答案#

常用方案:雪花算法(有序高性能)、号段模式(预分配+高可用)、Redis INCR(简单有序)、UUID(无序不推荐做主键)。

核心要点

为什么需要分布式 ID? 在分库分表或微服务架构下,数据库自增 ID 无法保证全局唯一,需要一种全局唯一的 ID 生成方案。

常见方案对比:

方案优点缺点
UUID本地生成,无网络开销,全球唯一无序(B+ 树插入性能差)、太长(128 位字符串)、不适合做主键
数据库自增简单、有序单点瓶颈、分库后不唯一、性能受限于 DB
数据库号段模式批量获取减少 DB 访问仍依赖 DB、号段用完需重新申请
Redis INCR性能高、有序依赖 Redis 可用性、持久化问题
Snowflake 雪花算法本地生成、有序、高性能依赖机器时钟、时钟回拨问题

Snowflake 雪花算法原理(64 位 Long 型 ID):

Snowflake 的时钟回拨问题:

问题:服务器时钟被回调(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——无序不适合做主键
  • ❌ 雪花算法不会出错——时钟回拨必须处理