MySQL主键是有序的好,还是无序的好?
📌 MySQL主键是有序的好,还是无序的好?
对于 InnoDB 存储引擎而言,主键尽量选择有序(递增),例如 AUTO_INCREMENT、Snowflake 趋势递增 ID,而不是 UUID 这种完全随机的主键。
B+Tree 本质上就是一棵有序多叉搜索树,ID逐渐增大,只需要在末尾添加就可以了,InnoDB 的数据是按照主键顺序组织存储(Clustered Index),主键是否有序,将直接影响:
- B+Tree 是否频繁分裂
- 磁盘页是否连续写入
- 缓存命中率
- 插入性能
- 磁盘碎片
- 整体查询效率
1️⃣ 问题背景
很多开发人员设计表结构时,都会纠结一个问题:
- 使用数据库自增ID?
- 使用UUID?
- 使用雪花ID?
- 使用业务ID?
很多人认为主键只是唯一标识即可,其实对于 InnoDB 来说,主键不仅仅是唯一索引,它还是整个数据文件的存储顺序,因此主键设计直接影响数据库性能。
2️⃣ 核心原理
InnoDB 采用的是聚簇索引(Clustered Index)。
所谓聚簇索引,就是:
- 索引和数据放在一起
- 整张表按照主键顺序存储
- 叶子节点保存完整的数据记录
因此,主键决定了数据在磁盘上的排列方式。
3️⃣ 数据结构分析
✅ 有序主键(AUTO_INCREMENT)
插入数据时,总是在最后一个数据页追加。
特点:
- 几乎不会发生页分裂
- 磁盘顺序写
- 缓存命中率高
- 插入效率最高
❌ 无序主键(UUID)
UUID 每次都会随机插入到 B+Tree 的任意位置。
可能插入到第一页,也可能插入到最后一页。
这样会导致:
- 页分裂(Page Split):当 B+Tree 的某个数据页已经满了,又要往里面插入新数据时,数据库不得不新申请一个数据页,把原来页中的一部分数据移动过去,然后更新 B+Tree 的结构。
- 页移动
- 大量随机IO:页分裂和页移动会导致Buffer pool失效,从而增加缓存失效
- 磁盘碎片增加:因为页分裂会产生新页面,页2分裂的数据到页30,不连续导致磁盘碎片
- 缓存污染:
4️⃣ 算法分析
下面分别看看两种插入方式。
有序主键插入
整个过程只需要找到最后一个叶子节点即可。
无序主键插入
如果当前页已满,则需要执行:
- 申请新的数据页
- 移动一半数据
- 修改父节点
- 重新维护 B+Tree
这就是经典的 Page Split(页分裂)。
5️⃣ 执行流程
6️⃣ 实际案例
案例一:订单表
每天新增几十万订单。
由于 ID 递增,所有数据都会写到最后一个页,插入效率非常高。
案例二:UUID 主键
UUID 每次都会随机写入,随着数据越来越多:
- 索引越来越碎
- 页分裂越来越频繁
- Buffer Pool 命中率下降
- 写入性能明显降低
UUID 并不是不能使用,而是不建议直接作为 InnoDB 聚簇主键。
如果业务必须使用 UUID,可以考虑:
- 增加一个 BIGINT 自增主键作为聚簇索引
- UUID 建唯一索引
- 使用趋势递增的 Snowflake ID、UUID v7、ULID 等方案,降低随机写带来的影响
7️⃣ 优缺点分析
| 对比项 | 有序主键 | 无序主键 |
|---|---|---|
| 插入性能 | ★★★★★ | ★★☆☆☆ |
| 页分裂 | 极少 | 频繁 |
| 随机IO | 少 | 多 |
| 磁盘碎片 | 少 | 多 |
| 缓存命中率 | 高 | 低 |
| 推荐程度 | ★★★★★ | ★★☆☆☆ |
8️⃣ 面试常见问题
Q1:为什么 InnoDB 推荐使用自增主键?
Q2:UUID 为什么性能差?
Q3:雪花ID是否属于有序主键?
Q4:什么时候可以使用 UUID?
9️⃣ 总结
- ✅ InnoDB 数据按照聚簇主键顺序存储,主键设计会影响整张表的组织方式。
- ✅ 有序主键采用顺序追加写入,可减少页分裂、降低随机 IO,并提升写入性能。
- ✅ 无序主键会导致频繁页分裂、索引碎片增多以及缓存命中率下降。
- ✅ 自增 ID、Snowflake ID、UUID v7、ULID 等趋势递增方案,更适合作为聚簇主键。
- ✅ 如果业务必须使用 UUID,建议额外增加 BIGINT 自增主键作为聚簇索引,将 UUID 建立唯一索引,以兼顾业务需求与数据库性能。
📌 MySQL主键是有序的好,还是无序的好?
对于 InnoDB 存储引擎而言,主键尽量选择有序(递增),例如 AUTO_INCREMENT、Snowflake 趋势递增 ID,而不是 UUID 这种完全随机的主键。
因为 InnoDB 的数据是按照主键顺序组织存储(Clustered Index),主键是否有序,将直接影响:
- B+Tree 是否频繁分裂
- 磁盘页是否连续写入
- 缓存命中率
- 插入性能
- 磁盘碎片
- 整体查询效率
1️⃣ 问题背景
很多开发人员设计表结构时,都会纠结一个问题:
- 使用数据库自增ID?
- 使用UUID?
- 使用雪花ID?
- 使用业务ID?
很多人认为主键只是唯一标识即可,其实对于 InnoDB 来说,主键不仅仅是唯一索引,它还是整个数据文件的存储顺序,因此主键设计直接影响数据库性能。
2️⃣ 核心原理
InnoDB 采用的是聚簇索引(Clustered Index)。
所谓聚簇索引,就是:
- 索引和数据放在一起
- 整张表按照主键顺序存储
- 叶子节点保存完整的数据记录
因此,主键决定了数据在磁盘上的排列方式。
3️⃣ 数据结构分析
✅ 有序主键(AUTO_INCREMENT)
插入数据时,总是在最后一个数据页追加。
特点:
- 几乎不会发生页分裂
- 磁盘顺序写
- 缓存命中率高
- 插入效率最高
❌ 无序主键(UUID)
UUID 每次都会随机插入到 B+Tree 的任意位置。
可能插入到第一页,也可能插入到最后一页。
这样会导致:
- 页分裂(Page Split)
- 页移动
- 大量随机IO
- 磁盘碎片增加
- 缓存污染
4️⃣ 算法分析
下面分别看看两种插入方式。
有序主键插入
整个过程只需要找到最后一个叶子节点即可。
无序主键插入
如果当前页已满,则需要执行:
- 申请新的数据页
- 移动一半数据
- 修改父节点
- 重新维护 B+Tree
这就是经典的 Page Split(页分裂)。
5️⃣ 执行流程
6️⃣ 实际案例
案例一:订单表
每天新增几十万订单。
由于 ID 递增,所有数据都会写到最后一个页,插入效率非常高。
案例二:UUID 主键
UUID 每次都会随机写入,随着数据越来越多:
- 索引越来越碎
- 页分裂越来越频繁
- Buffer Pool 命中率下降
- 写入性能明显降低
UUID 并不是不能使用,而是不建议直接作为 InnoDB 聚簇主键。
如果业务必须使用 UUID,可以考虑:
- 增加一个 BIGINT 自增主键作为聚簇索引
- UUID 建唯一索引
- 使用趋势递增的 Snowflake ID、UUID v7、ULID 等方案,降低随机写带来的影响
7️⃣ 优缺点分析
| 对比项 | 有序主键 | 无序主键 |
|---|---|---|
| 插入性能 | ★★★★★ | ★★☆☆☆ |
| 页分裂 | 极少 | 频繁 |
| 随机IO | 少 | 多 |
| 磁盘碎片 | 少 | 多 |
| 缓存命中率 | 高 | 低 |
| 推荐程度 | ★★★★★ | ★★☆☆☆ |
8️⃣ 面试常见问题
Q1:为什么 InnoDB 推荐使用自增主键?
Q2:UUID 为什么性能差?
Q3:雪花ID是否属于有序主键?
Q4:什么时候可以使用 UUID?
9️⃣ 总结
- ✅ InnoDB 数据按照聚簇主键顺序存储,主键设计会影响整张表的组织方式。
- ✅ 有序主键采用顺序追加写入,可减少页分裂、降低随机 IO,并提升写入性能。
- ✅ 无序主键会导致频繁页分裂、索引碎片增多以及缓存命中率下降。
- ✅ 自增 ID、Snowflake ID、UUID v7、ULID 等趋势递增方案,更适合作为聚簇主键。
- ✅ 如果业务必须使用 UUID,建议额外增加 BIGINT 自增主键作为聚簇索引,将 UUID 建立唯一索引,以兼顾业务需求与数据库性能。
UUID 作为主键时,由于键值随机,新记录会随机插入到 B+Tree 的不同叶子节点,而不是顺序追加到最后一个页。当目标页已满时,会触发页分裂,需要申请新页、迁移部分数据并更新父节点,增加了插入开销。同时,随机插入会导致访问的数据页分散,增加随机页访问和随机磁盘 IO 的概率;页分裂还会使物理页分配更加离散,增加磁盘碎片。另外,大量随机访问不同的数据页,会频繁替换 Buffer Pool 中的缓存页,降低缓存命中率,形成缓存污染。因此,InnoDB 通常推荐使用自增主键,以减少页分裂并提升写入性能。
下一篇:消息队列如何保证消息顺序消费?
相关文章
-
Dubbo 一次 RPC 请求执行流程
Dubbo 一次 RPC 请求执行流程
NEW个对象 2026-06-24
-
Git信息提交规范
Git提交信息格式:每次提交都应该有一个简明扼要的提交信息,格式为类型(范围):描述。常见的类型包括feat(新功能)、fix(修复Bug)、docs(文档修改)、style(代码格式修改)、test(测试相关修改)、refactor(重构代码)、chore(构建或工具相关的修改)等。
NEW个对象 2024-10-03
-
Spring遇到长事务该如何处理?一次彻底讲透长事务的危害与优化方案
Spring遇到长事务该如何处理?一次彻底讲透长事务的危害与优化方案
NEW个对象 2026-06-21