聚簇索引
InnoDB 使用聚簇索引组织行数据。本文讲解 MySQL 8.0 的聚簇键选择和维护成本。
1. 面试核心
先记住这几条,再读正文- 聚簇与二级索引。 InnoDB 聚簇索引叶子组织行数据,二级索引记录包含聚簇键。卡 · 聚簇索引
- 聚簇键选择。 InnoDB 优先用主键聚簇,其次选首个全列非空的唯一索引。卡 · 没有主键时的聚簇索引
- 隐藏行 ID。 缺少合适唯一键时,InnoDB 生成 6 字节隐藏行 ID。卡 · 没有主键时的聚簇索引
- 覆盖与版本检查。 覆盖索引满足取列需求,MVCC 检查仍可访问聚簇记录。卡 · 覆盖索引
- 主键更新。 更新主键需要维护聚簇记录和二级索引中的定位键。卡 · 主键更新
2. 聚簇索引
InnoDB 按聚簇键顺序组织叶子记录。叶子记录组织该行的所有列。DYNAMIC 行格式可将较长的变长值放入溢出页。叶子记录保存指向这些值的指针。
SELECT * FROM users WHERE id = 42;
若 id 是主键,查到聚簇叶子便能定位整行。存在页外值时,还需读取溢出页。
一张 InnoDB 表只有一个聚簇索引。键顺序是逻辑顺序,相邻页在磁盘上可以分散存放。
3. 没有主键时的聚簇索引
InnoDB 按以下顺序选择聚簇键:
- 表有显式主键时,使用主键。
- 否则使用第一个所有列都非空的唯一索引。
- 两者都缺少时,生成 6 字节隐藏行 ID。
隐藏聚簇索引名为 GEN_CLUST_INDEX。它的行 ID 由 InnoDB 内部维护。应用需要稳定引用一行时,应定义显式主键。
4. 二级索引与回表
二级索引包含聚簇键,查询缺失列时需要回表。访问步骤见联合索引与覆盖索引。
5. 覆盖索引
覆盖查询满足取列需求,版本判断仍可访问聚簇记录。机制见联合索引与覆盖索引。
6. 主键宽度
二级索引记录包含聚簇键。主键越宽,每个二级索引的定位信息就越大。同一页能容纳的记录更少,缓存也需承担更多空间。
短而稳定的键能减少这些成本。递增键把写入集中到右侧,随机键分散写入位置。页面调整的过程见 插入、删除与页分裂。
7. 主键更新
主键更新会改变行的聚簇键。InnoDB 要调整聚簇记录,并维护二级索引的定位信息。二级索引越多,维护范围越大。
业务编号经常变化时,可用稳定 ID 作为主键。业务编号另设唯一约束。编号修改便无需同时改变所有二级索引的定位键。
8. 范围查询与排序
主键范围查询可以沿聚簇叶子顺序读取。
SELECT * FROM users
WHERE id >= 10000 AND id < 10100;
二级索引范围查询需要完整行时,还需按聚簇键取列。返回行数很多时,回表成本随之增加。优化器据此比较二级索引访问和聚簇索引扫描。
9. 常见问题
9.1 主键与存储引擎
在 InnoDB 中,显式主键就是聚簇索引。其他存储引擎有各自的行组织方式。回答时应先说明数据库和存储引擎。
10 · 面试题卡片
本页题目与参考回答
10.1 InnoDB 聚簇索引和二级索引有什么区别,什么情况下会回表
考察重点
- 聚簇索引的行组织方式。
- 二级索引叶子中的主键与回表路径。
- 覆盖查询、主键宽度和范围读取。
参考回答
聚簇索引组织整行,二级索引组织索引列和聚簇键。
InnoDB 有显式主键时,用它作为聚簇键。二级索引缺少查询所需列时,需按聚簇键回表。
覆盖查询可省去取列回表,版本检查仍可访问聚簇记录。较宽的主键会增加二级索引占用。频繁修改主键也会扩大维护范围。
10.2 覆盖索引为什么仍可能访问聚簇索引?
考察重点
- 查询列覆盖与可见性
- 二级索引版本检查
相关内容:覆盖索引。
参考回答
覆盖索引只满足取列需求,MVCC 还需要判断版本可见性。
二级索引记录缺少完整版本信息。遇到删除标记或较新的页修改时,会检查聚簇记录。必要时再通过 undo 构造可见版本。
10.3 没有主键时 InnoDB 如何组织行,选择主键要考虑什么?
考察重点
- 聚簇键选择顺序
- 宽度、稳定性、写入分布
相关内容:没有主键时的聚簇索引、主键宽度。
参考回答
InnoDB 先选第一个全列非空的唯一索引。
没有合适索引时,使用隐藏的 6 字节行 ID。主键设计还要考虑宽度、稳定性和插入分布。显式主键便于应用、同步工具和迁移任务定位行。
10.4 为什么频繁修改主键会增加二级索引维护成本?
考察重点:聚簇键定位与稳定主键设计。
相关内容:主键更新。
参考回答
二级索引用聚簇键定位行,主键更新后定位键也要更新。
聚簇索引本身同样需要调整记录。业务编号易变时,可使用稳定 ID 聚簇。业务编号通过独立唯一约束保持唯一。