🗂️ 聚簇索引 vs 二级索引:回表与覆盖索引
为什么二级索引查询要回表?覆盖索引又怎么省掉这次回表?InnoDB 存储的核心概念。
聚簇索引:数据就在索引里
📚 InnoDB 主键 = 聚簇索引
聚簇索引(Clustered Index):InnoDB 的主键索引就是它。
数据行实际存储在索引的叶子节点上——「表即索引」。
一张表只能有一个聚簇索引,它决定了数据的物理存储顺序。
🔑 怎么选主键
InnoDB 建表逻辑:
① 有主键 → 用它当聚簇索引
② 没有主键 → 选一个 NOT NULL 唯一索引
③ 都没有 → 隐藏生成一个 rowid
所以尽量给表设主键。
二级索引:存主键值,查询要回表
🔖 二级索引 = 非聚簇索引
二级索引(Secondary Index)的叶子节点存的是主键值,而不是数据行本身。
所以通过二级索引查到主键值后,还要再回聚簇索引去拿完整数据——这个过程叫回表。
类比:书的附录索引只告诉你「词在哪一页」,你还得翻到那页看正文。
🔁 回表的代价
非覆盖查询:二级索引找到主键 → 回表到聚簇索引取数据。
至少 2 次 B+ 树搜索,而且回表是随机 I/O,数据量大时很慢。
这就是为什么要用覆盖索引。
覆盖索引:不用回表
🎯 覆盖索引 = 索引包含所有要的字段
当查询需要的所有字段(SELECT + WHERE + ORDER BY)都包含在索引里时,MySQL 只需扫描索引 B+ 树就能拿到全部数据,无需回表。
这就是覆盖索引(Covering Index),能省掉回表的随机 I/O,是查询优化的重要手段。
🔍 怎么判断
用 EXPLAIN 看输出:Extra 列显示 Using index 就说明用了覆盖索引。
覆盖索引的取舍
高频小查询
高频但只需要少数字段的查询,适合把常用字段建成联合索引实现覆盖。
统计查询
COUNT、SUM 等可以减少扫描数据量。
大表更显著
覆盖索引对大表性能提升非常明显(减少回表随机 I/O)。
别为覆盖而过度
不要为了覆盖把所有字段都加进索引——索引越大,维护成本越高。
InnoDB 主键索引是聚簇索引(数据存在索引叶子节点,表即索引,一张表只有一个);二级索引叶子存主键值,查询需先找到主键再回聚簇索引取数据(至少 2 次 B+ 树搜索 + 随机 I/O)。覆盖索引让查询字段全在索引里,EXPLAIN 显示 Using index,无需回表——是查询优化的重要手段。
聚簇索引的数据行实际存在索引的叶子节点上,表即索引,一张表只能有一个,InnoDB 里主键索引就是聚簇索引。二级索引的叶子节点存的是主键值而不是数据行本身,所以通过二级索引查到主键后,还要再回聚簇索引取完整数据,这个过程叫回表。打个比方,聚簇索引像书的正文按目录排好,二级索引像书的附录索引,只告诉你词在哪页,你还得翻到那页看正文。
回表就是通过二级索引找到主键值后,再回到聚簇索引去取完整数据行的过程。因为二级索引叶子只存主键值不存数据。回表代价高,要至少两次 B+ 树搜索,而且第二次是随机 I/O。避免回表的办法是用覆盖索引——让查询需要的所有字段都包含在索引里,这样只扫索引 B+ 树就能拿到全部数据,EXPLAIN 里 Extra 会显示 Using index。
覆盖索引是指一个索引包含了查询所需的全部字段,使得查询只需要扫描索引而无需回表。比如查询 SELECT a, b FROM t WHERE a=1,如果有个联合索引 (a,b),那 SELECT 和 WHERE 涉及的字段都在索引里,直接扫索引就拿齐了。判断方法是用 EXPLAIN,如果 Extra 显示 Using index 就是用了覆盖索引。它主要靠把高频查询的常用字段加进联合索引实现,但要注意别为覆盖把所有字段都加进去,索引越大维护成本越高。