聚簇索引与非聚簇索引有什么区别?
未标记
考查重点:叶节点内容与回表路径。以 InnoDB 为例,聚簇索引叶节点存整行,二级索引叶节点存主键;查询列不在二级索引中时,再按主键回表读取
本题目录
参考答案
核心回答
聚簇索引与非聚簇索引的主要区别是索引如何组织行数据,以及找到索引记录后怎样定位完整行。以 MySQL 8.4 InnoDB 的普通 B-tree 索引为例:
- 聚簇索引的叶子记录保存行数据。 通常按主键组织,沿主键查到叶子记录,就定位到了这一行。每张
InnoDB表只有一种聚簇组织。 - 二级索引的叶子记录保存索引列和定位行用的主键值。 用二级索引查完整行时,通常先找到主键,再访问聚簇索引,这一步叫回表。一张表可以有多个二级索引。
- 二级索引不一定需要回表取列。 如果查询所需列都在该索引中,可以使用覆盖索引;但实际访问还可能受事务可见性检查影响。
这是数据组织维度的划分,与唯一、普通、单列、联合等分类可以重叠。也不能把所有数据库的非聚簇索引都理解为“存主键”:例如 MyISAM 的索引使用数据记录地址定位行,与 InnoDB 二级索引不同。
用同一张商品表说明查找路径
假设商品有 id、商品编码和价格,id 是主键,商品编码上另建二级索引:
- 按 id 查询价格: 查聚簇索引,找到行记录后读取价格。
- 按商品编码查询价格: 二级索引找到商品编码和对应 id,再用 id 查询聚簇记录取得价格。
- 按商品编码只查询 id: 二级索引本来就包含 id,满足取列需要,具备覆盖查询的条件。
因此,二级索引叶子并非“只保存主键”,索引列自身也在其中。覆盖与否取决于具体查询;同一个索引对“只取 id”和“还取价格”可以有不同的作用。
覆盖也不是任何事务场景下都绝不访问聚簇记录。InnoDB 对某些删除标记或被较新事务更新的二级索引页,需要访问聚簇记录并判断可见版本。索引条件下推则是尽早用索引列过滤,和“取列已被覆盖”是不同的优化。
没有主键,是否就没有聚簇索引
不是。InnoDB 优先使用显式主键;没有主键时,选择第一个所有键列均为 NOT NULL 的唯一索引;仍没有合适索引时,生成隐藏行 ID 和聚簇索引。
隐藏行 ID 是引擎内部的组织方式,不等于给应用自动增加了可见的业务主键。表仍应根据业务和访问需求设计明确的主键。
这些差异怎样影响设计
较短、稳定的主键通常能减小二级索引,因为二级记录还需要携带主键。频繁修改主键会影响行组织及相应二级索引维护;随机插入可能增加页分裂等成本,但不应概括为 UUID 一定不可用、自增一定不分裂。
聚簇结构按键组织记录与叶子页的访问顺序,并不保证相关页在磁盘上始终连续,也不保证没有 ORDER BY 的 SQL 返回有序结果。主键范围读取通常有利于连续遍历记录;二级索引范围查询若需要大量回表,仍可能成本较高。
回表表示额外的逻辑查找路径,不能固定说成“多一次磁盘 I/O”。相关页可能已在 Buffer Pool 中,实际成本还取决于命中记录数、缓存命中情况和所需列。设计覆盖索引也要权衡索引宽度、存储与写入维护,不能把所有列都塞进去。
参考资料
- 国内:JavaGuide:MySQL 索引详解:用于比较 InnoDB 与 MyISAM 的行定位方式,以及聚簇、二级和覆盖索引的关系。
- 国内:小林 coding:索引常见面试题:采用主键、二级索引回表与覆盖查询的讲解,解释正文中商品表的三条访问路径及其行定位方式。
- 国内:美团技术团队:MySQL 索引原理及慢查询优化:借鉴页式多路搜索和有序访问的成本分析,解释正文中索引宽度、回表与查询需求的取舍。
- 国外:MySQL 8.4:Clustered and Secondary Indexes:用于核对聚簇键选择顺序、二级记录包含的列,以及短主键对二级索引空间的影响。
- 国外:MySQL 8.4:The Physical Structure of an InnoDB Index:用于核对索引的页式存储和叶子记录组织,以及插入、页填充与结构维护的条件。







