二级索引叶子节点只存主键值,不存其他列;执行select *时需回表查聚簇索引;explain中无using index且含非索引列即回表;联合索引需严格匹配最左前缀才能避免回表;回表是innodb聚簇索引设计的必然结果。

二级索引叶子节点只存主键值,不存其他列
InnoDB 的二级索引(比如 INDEX idx_name (name))本质是一棵独立的 B+Tree,它的叶子节点只保存两样东西:索引列值(如 'Tom')和对应的主键值(如 id = 15)。它**不存**age、email等其他字段。
所以当你执行 SELECT * FROM users WHERE name = 'Tom',MySQL 先在 idx_name 树里找到 'Tom' → 拿到 id = 15;但这时还缺 age 和 email,只能拿着 id = 15 去聚簇索引(主键索引)再查一次——这就是回表。
EXPLAIN 中看不到 Using index 就大概率要回表
EXPLAIN 的 Extra 字段是判断是否回表最直接的依据:
- 出现
Using index:说明走的是覆盖索引,所有需要的列都在二级索引里,不回表 - 没有
Using index,且SELECT的列包含非索引字段(比如SELECT name, age但只有INDEX idx_name (name)),基本一定回表 -
Using index condition是 ICP(索引条件下推),它能减少回表行数,但不能消除回表——只要最终要取的列不在索引中,仍需回表
联合索引能避免回表,但顺序和查询列必须严格匹配
比如建了 INDEX idx_name_age (name, age),下面这些查询行为不同:
-
SELECT name, age FROM users WHERE name = 'Tom'→ 覆盖索引,Extra显示Using index,不回表 -
SELECT name, age, email FROM users WHERE name = 'Tom'→email不在索引里,必须回表 -
SELECT age FROM users WHERE name = 'Tom'→ 可以用索引,但age是第二列,name是第一列,B+Tree 仍能定位,不回表 -
SELECT age FROM users WHERE age = 25→age不是索引最左前缀,无法使用该联合索引,可能走全表或别的索引,和回表无关
回表不是 bug,是 InnoDB 存储结构决定的硬约束
回表不是优化没做好才出现的“异常”,而是 InnoDB 聚簇索引设计下的必然行为。只要满足两个条件就会触发:
- 查询用了二级索引(非主键索引)
-
SELECT列中至少有一个不在该二级索引的定义列里
哪怕只多查一个 VARCHAR 字段,也会触发整行回表。很多人误以为“只差一列,代价不大”,但实际是:每条匹配记录都要单独访问一次聚簇索引页,随机 IO 成倍放大,尤其在 WHERE 返回几百上千行时,性能断崖式下降。











