分布式唯一 ID:为什么 UUID 不够用?

分库分表之后,单库自增主键不再能保证全局唯一;而 UUID 虽然生成简单,却存在空间占用大、写入索引不友好等问题。真正适合亿级流量系统的唯一 ID,需要在全局唯一、空间占用、高并发、高可用和数据库主键友好之间取得平衡。

一、核心主线

在分库分表和分布式系统中,如何生成全局唯一、性能高、可用性强且适合作为数据库主键的 ID。

整体演进逻辑:

1
2
3
4
5
6
7
8
9
单体数据库自增主键
↓
分库分表后自增主键可能重复
↓
需要全局唯一 ID
↓
UUID 或分布式唯一 ID
↓
进一步要求:短、快、可用、递增、主键友好

本节重点明确唯一 ID 的设计目标,并区分了单调递增和趋势递增两种类型。


二、为什么需要分布式唯一 ID

1. 单体数据库中的自增主键

在单体架构中,一个业务实体通常对应一张数据表。

1
2
3
用户表 → 数据库自增主键
内容表 → 数据库自增主键
评论表 → 数据库自增主键

由于所有数据都在同一个数据库中,数据库自增主键可以保证表内唯一。

2. 分库分表后的问题

随着用户量增长,数据库会进行分库分表。

1
2
3
4
用户数据
├── 数据库 1
├── 数据库 2
└── 数据库 3

每个数据库拥有独立的自增序列:

1
2
3
数据库 1:1、2、3...
数据库 2:1、2、3...
数据库 3:1、2、3...

不同数据库可能生成相同的 ID。

如果仍将数据库自增主键作为业务实体的唯一 ID,就会出现:

  • 不同数据拥有相同标识;
  • 跨库查询无法准确定位数据;
  • 数据关联关系错误;
  • 业务产生严重事故。

因此,需要一种跨数据库、跨节点仍然唯一的 ID:

分布式唯一 ID。


三、UUID

1. 基本概念

RFC 4122 定义了 UUID(Universally Unique Identifier,通用唯一识别码)。

UUID 是一个 128 位标识符,按照标准方法生成时,重复概率极低。

标准字符串格式:

1
8-4-4-4-12

例如:

1
6a0d3e6f-a11c-4b7d-bb35-c4c530a456b0

2. UUID 的优势

  • 不依赖中心服务;
  • 本地即可生成;
  • 不需要访问数据库;
  • 生成逻辑简单;
  • 全局重复概率极低。

3. UUID 的局限

3.1 存储空间较大

UUID 字符串通常占用 36 个字符。

如果海量数据的每条记录都保存 UUID:

1
数据量巨大 × 每条 36 字节

会产生较大的存储空间消耗。

3.2 不适合作为数据库主键

UUID 是无规律的长字符串,用作数据库主键时,新数据的 Key 可能随机插入索引中间位置。

这会导致:

  • 数据页频繁移动;
  • B+ 树分页;
  • 磁盘碎片增加;
  • 写入性能下降。

因此,本节认为 UUID 更适合:

  • 数据量不大的场景;
  • 标识存储分区;
  • 不直接作为海量业务表主键的场景。

四、海量场景下唯一 ID 的设计要求

真正适合大型互联网系统的唯一 ID 生成器,除了保证不重复,还应具备以下特点。

1. 空间占用小

唯一 ID 通常会被每条业务数据携带,因此不应占用过多存储空间。

书中倾向于使用:

1
64 位 long 类型整数

它只占用 8 字节,同时可表示很大的数值范围。

2. 高并发

唯一 ID 生成器是大量业务服务的基础依赖。

它必须能够:

  • 承受高并发生成请求;
  • 低延迟返回 ID;
  • 避免成为业务链路瓶颈。

3. 高可用

如果唯一 ID 生成器不可用,依赖它的用户、内容、订单等业务都可能无法创建新数据。

因此需要:

  • 持续稳定提供服务;
  • 避免单点故障;
  • 支持故障恢复;
  • 保证 ID 分配不重复。

4. 适合作为数据库主键

唯一 ID 不应严重影响数据库写入性能。

理想情况下,ID 应具有递增特征,使数据库索引能够顺序写入。


五、InnoDB 主键与 B+ 树

1. InnoDB 的索引结构

MySQL InnoDB 使用磁盘上的 B+ 树组织数据,并以主键作为索引。

B+ 树的特点:

  • 节点使用数据页保存;
  • 默认数据页大小约为 16 KB;
  • 节点分为叶节点和非叶节点;
  • 非叶节点保存主键和子页索引;
  • 叶节点保存实际数据;
  • 同层叶节点通过双向链表连接;
  • 数据按照主键从小到大排列。

2. 递增主键的好处

使用自增或递增 ID 作为主键:

1
2
3
4
5
新数据
↓
顺序追加到索引尾部
↓
数据页写满后创建新页

优点:

  • 数据顺序写入;
  • 索引结构更紧凑;
  • 空间利用率更高;
  • 不需要频繁移动旧数据;
  • 分页操作较少;
  • 写入性能较好。

3. 随机主键的问题

如果使用身份证号、电话号码或 UUID 等随机值作为主键:

1
新 Key 可能插入 B+ 树中间

可能导致:

  • 已有记录向后移动;
  • 数据页频繁分页;
  • 磁盘产生更多碎片;
  • 写入耗时增加;
  • 索引空间利用率下降。

因此,数据库主键最好使用递增性质的 ID。


六、单调递增与趋势递增

1. 单调递增

单调递增表示:

后一次生成的 ID 一定大于前一次生成的 ID。

形式化表示:

1
2
3
T(n+1) > T(n)
⇒
F(T(n+1)) > F(T(n))

特点:

  • 全局严格有序;
  • 任何下一次生成的 ID 都比上一次大;
  • 最适合严格顺序需求;
  • 分布式实现复杂;
  • 容易受到全局时钟、网络延迟和并发调度影响。

2. 趋势递增

趋势递增表示:

后生成的 ID 大概率比先生成的 ID 大,但短时间内可能出现乱序。

形式化理解:

1
2
3
T(n+1) > T(n)
⇒
F(T(n+1)) 大概率 > F(T(n))

特点:

  • 整体趋势递增;
  • 局部允许少量乱序;
  • 更适合分布式系统;
  • 实现成本和性能通常更好;
  • 对数据库主键仍然比较友好。

3. 为什么业界更偏好趋势递增

在分布式环境中:

  • 各节点时钟可能存在差异;
  • 网络延迟不同;
  • 请求到达顺序和完成顺序可能不同;
  • 多节点并发生成会导致严格全局排序困难。

因此,严格单调递增通常需要更复杂的协调机制。

趋势递增允许少量局部乱序,但能更好地平衡:

  • 分布式生成;
  • 高并发;
  • 高可用;
  • 数据库写入性能。

七、唯一 ID 类型对比

类型 唯一性 递增性 空间占用 生成方式 适用情况
数据库自增主键 单库内唯一 单调递增 小 依赖数据库 单体或单库场景
UUID 全局唯一概率极高 无序 较大 本地生成 低数据量、非主键标识
单调递增 ID 全局唯一且严格递增 严格递增 可较小 需要中心协调 强顺序需求
趋势递增 ID 全局唯一 整体递增、局部可能乱序 可较小 分布式生成 海量数据和高并发场景

八、唯一 ID 生成器的设计方向

1
2
3
4
5
6
7
8
9
10
11
生成 64 位 long 类型
+
保证全局唯一
+
尽量递增
+
支持高并发
+
具备高可用
+
适合作为数据库主键

九、核心架构思想

  1. 分库分表后,单库自增主键无法保证全局唯一。
  2. 分布式唯一 ID 必须跨数据库、跨节点保持不重复。
  3. UUID 本地生成简单,但空间占用大且不适合作为海量数据库主键。
  4. 唯一 ID 生成器不仅要唯一,还要满足低空间、高并发、高可用和主键友好。
  5. InnoDB B+ 树更适合使用递增主键进行顺序写入。
  6. 随机主键会增加数据移动、分页、碎片和磁盘 I/O。
  7. 64 位 long 类型在空间占用和可表示范围之间取得了较好平衡。
  8. 单调递增要求全局严格有序,分布式实现复杂。
  9. 趋势递增允许局部乱序,但整体递增,更适合互联网分布式系统。
  10. 唯一 ID 生成方案的核心权衡是唯一性、顺序性、性能、可用性和实现复杂度。

十、一句话总结

分库分表后必须使用跨节点全局唯一的分布式 ID;UUID 虽然生成简单但空间大且不利于数据库写入,适合海量业务数据的 ID 应尽量使用 64 位整数,并具备高并发、高可用和递增特征,其中严格单调递增实现复杂,而允许局部乱序的趋势递增更适合分布式系统和 InnoDB 主键。

© 2026 DadaVinCi's Blog

Elegant theme by Shiro · Made by Acris with ❤️