索引覆盖指查询仅通过索引B+树叶子节点获取全部数据,无需回表;EXPLAIN中出现Using index即表示成功覆盖。
判断一个SQL查询是否能走索引覆盖,核心看
SELECT字段和WHERE条件字段是否全部被同一个索引包含
,且该索引的列顺序满足最左前缀原则;一旦需要回表,说明索引中缺失了查询所需的部分列(尤其是SELECT中的非索引列或ORDER BY/GROUP BY中未被覆盖的列)。
什么是索引覆盖?
当一条查询语句执行时,
仅通过B+树的叶子节点就能获取所有需要的数据,无需回主键索引(聚簇索引)查找整行记录
,就称为“索引覆盖”。此时执行计划中会出现
Using index
标识。
例如:表 user(id PK, name, age, city),建立联合索引 (city, age, name);
执行
→ 可索引覆盖;
但
→ 不一定覆盖(id 是主键,InnoDB 中二级索引叶子节点存的是主键值,所以仍可能免回表;但若查的是
这类非索引列,必然回表)。
什么情况下一定会触发回表?
只要查询中出现
任何未被当前使用索引包含的列
(包括 SELECT、ORDER BY、GROUP BY、HAVING 中引用的列),且该列不属于该索引定义的一部分,就会触发回表。
SELECT *
几乎总是回表(除非是唯一索引且表只有几列,还极少见);
WHERE 条件用了索引,但 SELECT 中有额外列:如索引是 (a,b),却查
→ c 导致回表;
ORDER BY 字段不在索引中,或顺序/方向不匹配:如索引 (a,b ASC),却
→ 可能无法利用索引排序,优化器可能放弃覆盖而选择回表+文件排序;
使用了函数或表达式:如
,即使 create_time 有索引,也无法用上索引覆盖(索引失效+需计算后比对)。
如何验证是否发生索引覆盖?
直接看
结果中的
Extra
列:
出现
Using index
→ 索引覆盖成功;
出现
Using where; Using index
→ 也是覆盖(WHERE 在索引上完成过滤,结果直接从叶子节点读出);
出现
Using where; Using index condition
→ 是ICP(索引下推),部分过滤在存储引擎层做,但仍属覆盖范畴;
只写
Using where
或
Using filesort
/
Using temporary
→ 大概率未覆盖,已回表。
避免回表的实用建议
不是索引越多越好,而是要让高频查询的“查询列 + 过滤列 + 排序列”尽量落在同一索引中:
把
WHERE 等值条件列放最左
,范围查询列(如 >, LIKE 'abc%')靠右,SELECT 和 ORDER BY 列追加在最后;
避免在索引列上使用函数、类型
隐式转换
、NOT、!=、IS NULL 等易导致索引失效的操作;
对宽表(列多)且 QPS 高的查询,可考虑单独建覆盖索引,哪怕冗余,也比回表带来的随机IO更高效;
注意索引长度:过长的 VARCHAR 或 TEXT 建索引会拖慢性能,必要时用前缀索引 + 覆盖权衡。
SELECT name, age FROM user WHERE city = 'Beijing'SELECT name, age, id FROM user WHERE city = 'Beijing'emailSELECT a,b,cORDER BY a, b DESCWHERE YEAR(create_time) = 2023EXPLAIN