mysql中覆盖索引的概念与优化技巧

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

覆盖索引是什么,为什么能跳过聚簇索引查找

覆盖索引不是一种特殊类型的索引,而是指一个查询所需的所有字段,全部被某个索引的列(包括键列和

INCLUDE
列,但 MySQL 不支持
INCLUDE
,所以只能靠联合索引的“最左前缀 + 额外列”)直接提供,无需回表查聚簇索引。MySQL 的 InnoDB 中,二级索引叶子节点只存主键值;如果查询字段都在该二级索引中,就不用再拿主键去聚簇索引里捞整行数据——这省掉了至少一次随机 I/O。

典型表现是

EXPLAIN
输出中
Extra
列出现
Using index
,而不是
Using index condition
或空值。

如何构造有效的覆盖索引(以 SELECT + WHERE 场景为例)

关键在于让

WHERE
条件列 +
SELECT
列全部落在同一个联合索引中,且满足最左前缀原则。顺序设计直接影响是否生效:

把等值查询列(
=
IN
)放最左,提升索引复用率
范围查询列(
>
BETWEEN
LIKE 'abc%'
)放等值列之后,但不能再往后加其他列用于覆盖(最左前缀在此中断)
所有
SELECT
字段必须包含在索引定义中,哪怕只是
ORDER BY
GROUP BY
用到的字段

例如:查询

SELECT user_id, status, created_at FROM orders WHERE shop_id = 123 AND status = 'paid' ORDER BY created_at DESC;
覆盖索引应建为:
ALTER TABLE orders ADD INDEX idx_shop_status_created (shop_id, status, created_at, user_id);
注意:这里把
user_id
放最后,是因为它不出现在
WHERE
ORDER BY
中,仅用于返回,且不破坏最左前缀匹配。

常见踩坑:哪些情况看似覆盖,实则失效

覆盖索引对写法敏感,稍有不慎就会退化为回表。以下情况会直接导致

Using index
消失:

SELECT *
—— 即使索引包含多列,也无法覆盖所有列(除非表只有这些列且索引含全部)
使用了未包含在索引中的函数或表达式,如
SELECT UPPER(name)
,即使
name
在索引里也不行
WHERE
中用了索引列的非最左前缀访问,比如索引是
(a, b, c)
,却只查
WHERE b = 1
存在隐式类型转换,如索引列是
VARCHAR
,而查询传入数字:
WHERE mobile = 13800138000
→ MySQL 可能放弃走索引
OR
条件混用不同索引列,优化器可能选择全表扫描或只用部分索引

用 EXPLAIN 验证覆盖是否真正生效

不能只看

key
字段是否用了索引,必须检查
Extra
是否为
Using index
。同时注意区分相似提示:

Using index
✅:纯覆盖,不回表
Using index condition
⚠️:用了 ICP(索引下推),但仍有回表(因为
SELECT
字段不在索引中)
Using where; Using index
✅:也是覆盖,
where
过滤由存储引擎完成,常见于
WHERE
条件全命中索引

执行时建议加

FORMAT=JSON
查看详细信息,尤其是
used_columns
attached_condition
字段,能明确看出哪些列被索引服务、哪些仍需回表过滤。

覆盖索引虽快,但会让索引体积变大,写入开销上升;字段顺序错一位,或者漏掉一个

ORDER BY
列,就前功尽弃。实际建索引前,一定先用
EXPLAIN
对真实查询语句做验证,别依赖“看起来应该可以”。

相关推荐