题目:什么是回表,怎么减少回表的次数?
难度:low
标签:db/mysql/index, mysql/index, difficulty/low
评分:⭐⭐⭐☆(3/5)
所谓回表,就是使用索引,非主键索引去查询数据。查询的数据到叶子层其实是没有主数据的,这时候它的数据需要从主数据去拿,这个拿的过程就是回表。回表就是通过子索引找到主键 ID,然后再通过主键 ID 去聚簇索引里边拿到主数据,然后展现出来。想要缩减回表的话,首先可以做覆盖索引,也就是说将要展示的数据统一加到索引里边来,在索引的底层直接可以拿到整体的数据,这样可以减少回表的次数。另外就是降低查询的数据,少量的数据获取也可以降低回表,因为回表是通过数据去查主表、主键索引的位置。数据量少了,对应的回表次数也就少了。
优点:
不足:
LIMIT + 延迟关联 这个高频实战手法补充:
SELECT * FROM t INNER JOIN (SELECT id FROM t WHERE ... ORDER BY x LIMIT 10) tmp ON t.id = tmp.id核心考点定位:理解 InnoDB 二级索引与聚簇索引的关系,掌握减少回表的工程手法。
知识点拆解:
EXPLAIN 的 Extra 显示 Using index。EXPLAIN 显示 Using index condition。考察方向:
EXPLAIN 识别回表并给出具体的 SQL 改写方案延伸递进面试题:
Extra 列里的 Using index 和 Using index condition 有什么区别?Using index 是覆盖索引,完全在二级索引内解决;Using index condition 是索引下推,还在索引阶段做过滤但需要回表取其他列。SELECT * FROM t WHERE a=? ORDER BY b LIMIT 10 有索引 (a,b),还会有回表吗?如何彻底消除?**SELECT * 导致覆盖失败,回表 10 次。若要彻底消除,要么建覆盖索引包含所有 SELECT 列,要么用延迟关联。面试官您好,回表是 InnoDB 的一个特有现象。InnoDB 把数据存在聚簇索引里,而二级索引的叶子节点只存"索引列 + 主键 ID",不存完整行。所以当我们用二级索引查数据时,先在二级索引的 B+ 树上定位到主键 ID,再拿这个 ID 回聚簇索引 B+ 树查一次完整行,这次回头查主表的过程就叫回表。回表贵,是因为每一次都是一次独立的 B+ 树查找,如果行数据不在 Buffer Pool 里,就是随机磁盘 IO;而且回表次数取决于索引扫描到的行数,不是最终 LIMIT 数量,一条 LIMIT 10 的查询可能回表几百万次。
减少回表主要有三个手段。第一是覆盖索引,把 SELECT 需要的列都放进一个索引里,这样在二级索引叶子节点就能拿到全部数据,EXPLAIN 里会显示 Using index,完全不需要回表。第二是延迟关联,写法是先在一个子查询里只查主键 ID,配合 WHERE 和 ORDER BY 和 LIMIT,然后再拿这批主键 JOIN 回主表拿完整行,这样回表次数就被压到 LIMIT 那个数。第三是索引下推 ICP,这是 MySQL 5.6 加的特性,对于范围查询,把非索引列的过滤条件推到索引扫描阶段做,减少需要回表的行数,EXPLAIN 里能看到 Using index condition。
工程上还要注意几个坑,一是不要在索引里放大字段和长字符串,会让索引膨胀;二是尽量避免 SELECT *,它会让覆盖索引失效;三是主键最好用自增整数,UUID 会让二级索引里存的主键无序,回表时随机 IO 更多。
https://chat.deepseek.com/share/5e7y27yvn1p1s5d5s1