mysql中覆盖索引与普通索引的性能对比

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

覆盖索引为什么能避免回表

MySQL 的二级索引(非聚簇索引)只存储索引列 + 主键值,不存整行数据。当

SELECT
的所有字段都包含在某个索引中时,优化器可以直接从该索引页拿到全部所需数据,无需再用主键去聚簇索引里查一遍——这个“免去二次查找”的过程就是“避免回表”。普通索引若缺失部分
SELECT
字段(尤其是非索引列),就必须回表,I/O 和 CPU 开销明显上升。

怎么判断一个查询是否走覆盖索引

EXPLAIN
Extra
列:

出现
Using index
→ 覆盖索引生效
只有
Using where
Using index condition
→ 没覆盖,可能回表
同时出现
Using index; Using where
→ 覆盖索引 + 条件过滤(仍不回表)

注意:即使用了

WHERE
,只要所有
SELECT
字段和
WHERE
字段都在同一索引里,仍可覆盖。例如表
t(user_id, status, create_time)
,执行
SELECT user_id, status FROM t WHERE create_time > '2023-01-01'
就不会覆盖——因为
create_time
在索引里,但没被选中;而
SELECT user_id, create_time FROM t WHERE status = 1
可能覆盖,取决于索引定义顺序。

联合索引顺序对覆盖效果的影响

覆盖能力高度依赖索引列顺序。MySQL 按最左前缀匹配,且索引页内按定义顺序排序存储:

INDEX idx_a_b_c (a, b, c)
可覆盖:
SELECT a
SELECT a,b
SELECT a,b,c
SELECT a,c WHERE b = ?
(注意:b 在中间,c 无法跳过 b 使用索引排序,但覆盖仍成立)
SELECT b,c
不会走该索引覆盖,因缺失最左列
a
若常查
b,c
,应建
INDEX idx_b_c (b, c)
,而非依赖旧索引

另外,

ORDER BY
GROUP BY
字段若不在覆盖索引中,即使
SELECT
覆盖了,也可能被迫排序或临时表,实际性能未必好。

覆盖索引的代价和误用风险

它不是银弹。每多一列进索引,B+ 树节点就更臃肿:

写入变慢:INSERT/UPDATE 需维护更多索引数据 空间翻倍:比如给大文本列加进索引,索引体积可能超过原表 缓存压力:更大的索引页降低 buffer pool 命中率 隐式类型转换会让覆盖失效:比如
WHERE status = '1'
(status 是 INT),触发隐式转换,可能导致索引无法使用,更别说覆盖

真正关键的是:别为了“看起来用了索引”而堆字段。先确认查询模式,再按

WHERE → ORDER BY → SELECT
优先级设计索引列顺序,最后看是否自然覆盖。强行把 5 个字段塞进一个索引,往往不如两个精简索引管用。

相关推荐