mysql执行SQL过程中会发生回表吗_索引执行流程说明

来源:这里教程网 时间:2026-02-28 20:37:11 作者:

MySQL 什么时候会回表?

会,只要

SELECT
的字段不在当前使用的索引中全部覆盖,且引擎是 InnoDB,就大概率发生回表。回表不是语法行为,而是 InnoDB 在二级索引(非聚簇索引)查到主键后,再拿着主键去聚簇索引(即主键索引)里捞完整行数据的过程。

典型触发场景:

WHERE
条件走了二级索引,但
SELECT
里包含了该索引没包含的列(比如
SELECT name, email FROM users WHERE city = 'Beijing'
,而
city
是单独的二级索引)。

只有
SELECT *
或含非索引列时才可能回表;如果
SELECT
的全是索引列(包括联合索引中的部分或全部),就走“索引覆盖”,不回表
ORDER BY
GROUP BY
涉及非索引列,也可能迫使回表(即使
WHERE
匹配了索引)
使用
LIKE 'xxx%'
走了索引,但选了非索引列 → 同样回表

如何判断一条 SQL 是否回表?

EXPLAIN
输出里的
Extra
字段:

出现
Using index
:说明走了索引覆盖,没回表
出现
Using where; Using index
:也属于索引覆盖(
WHERE
在索引上完成,无需回表取数据)
只出现
Using where
(没带
Using index
):大概率回表了
出现
Using index condition
:用了 ICP(索引下推),仍可能回表——ICP 只是把部分
WHERE
过滤下推到存储引擎层,不代表不取整行
EXPLAIN SELECT id, name FROM users WHERE city = 'Shanghai';

city
是普通索引,而
name
不在该索引中,则
Extra
里不会出现
Using index
,实际执行时就会回表。

联合索引怎么影响回表?

联合索引的顺序和

SELECT
列是否被“覆盖”,直接决定回表与否。例如建立索引
INDEX idx_city_name_age (city, name, age)

SELECT city, name FROM ... WHERE city = 'X'
Using index
,不回表
SELECT city, name, age FROM ... WHERE city = 'X'
→ 仍不回表(全在索引里)
SELECT city, name, email FROM ... WHERE city = 'X'
→ 回表(
email
不在索引中)
SELECT name FROM ... WHERE age = 25
→ 无法用该联合索引(最左前缀不匹配),可能走全表或其它索引,与回表无关

注意:

ORDER BY city, name
可利用该索引避免文件排序;但
ORDER BY name
单独出现,无法利用,可能触发回表+临时表+filesort。

回表对性能的影响有多大?

一次回表 ≈ 一次随机主键查找(B+ 树搜索),在 SSD 上约 0.1–0.3ms,在 HDD 上可能达几毫秒。当扫描 10 万行二级索引记录,就要额外做 10 万次主键查找 —— IO 放大严重,极易成为瓶颈。

回表不可缓存:每次都要访问聚簇索引页,Buffer Pool 命中率低 并发高时,大量回表加剧 Buffer Pool 和磁盘争用 优化方向优先是“让查询走覆盖索引”,而不是盲目加索引;加宽联合索引前需评估写入开销和维护成本
innodb_read_ahead_threshold
等参数对回表无实质缓解作用,它只影响预读,不改变回表逻辑

真正容易被忽略的是:开发常以为「有索引=快」,却没检查

EXPLAIN
Extra
列,也没验证
SELECT
列是否被索引完全覆盖——回表往往静默发生,直到慢查询日志爆发才被发现。

相关推荐