mysql覆盖索引是什么_mysql查询优化技巧

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

覆盖索引就是“查什么,索引里就有什么”

MySQL 执行查询时,如果

SELECT
列和
WHERE
条件列全部被一个索引“包圆”了,连回表(去聚簇索引找完整行)都不用,那就叫覆盖索引。它不是某种特殊索引类型,而是**一种使用方式**——你建的普通 B+Tree 索引,只要字段选得准,就能变成覆盖索引。

关键判断依据是

EXPLAIN
输出中的
Extra
列:出现
Using index
就说明命中了覆盖索引;如果写
SELECT *
或漏了某列,哪怕只差一个字段,也会退化为
Using index condition
或更糟的
Using where; Using filesort

怎么建才能让索引真正“覆盖”你的查询

核心原则是:把

WHERE
条件字段放前面,
SELECT
字段放后面,且不冗余、不遗漏。

错误示范:
CREATE INDEX idx_on_age_name ON users(age, name)
用于
SELECT name, city FROM users WHERE age > 25
——
city
没进索引,必然回表
正确做法:
CREATE INDEX idx_on_age_name_city ON users(age, name, city)
,顺序按“过滤 → 排序 → 返回”逻辑组织
注意:InnoDB 的二级索引叶子节点天然存主键值,所以如果你查的是
SELECT id, name FROM users WHERE age = 30
,即使索引只建了
(age, name)
,也能覆盖——因为
id
是主键,自动附带
别用
SELECT *
去“赌”覆盖,它会让优化器直接放弃走索引,尤其当表有大字段(如
TEXT
)时,索引体积爆炸,得不偿失

为什么有时候明明建了索引,
EXPLAIN
却不显示
Using index

常见原因不是索引没建对,而是查询本身“破坏”了覆盖条件:

ORDER BY
字段不在索引中或顺序不匹配:比如索引是
(status, created_at)
,但查询写
ORDER BY created_at DESC
,而索引默认升序,可能导致无法利用索引排序,触发文件排序(
Using filesort
),进而中断覆盖
隐式类型转换:字段是
VARCHAR
WHERE
条件却传数字(如
WHERE mobile = 13800138000
),MySQL 会强制转类型,导致索引失效
对索引列用了函数或运算:
WHERE YEAR(create_time) = 2025
WHERE price * 1.1 > 100
,索引值被修改,无法直接比对
使用了
OR
且部分分支无索引:哪怕左边字段有索引,右边没有,整个条件大概率走全表扫描

覆盖索引不是万能的,别在这些场景硬上

它省的是 I/O,但代价是索引体积变大、写入变慢、内存占用升高。以下情况要谨慎:

查询字段太多(比如 8 个以上),尤其是含大文本字段——索引可能比数据还大,缓存效率反而下降 写多读少的表:每条
INSERT/UPDATE
都要维护多个覆盖索引,性能损耗明显
高频
LIKE '%keyword'
查询:B+Tree 对前导通配符无能为力,覆盖索引也救不了
需要
GROUP BY
+ 聚合函数(如
SUM()
)且分组字段未覆盖:聚合仍需扫描原始行,覆盖索引意义有限

最常被忽略的一点:覆盖索引对

SELECT COUNT(*)
有奇效(InnoDB 可直接扫索引统计),但对
SELECT COUNT(col)
col
允许 NULL)则不一定——它得确认该列非空,可能仍需回表校验。

相关推荐