mysql覆盖索引如何减少回表_mysql性能优化方法

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

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

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

SELECT
的所有字段都包含在某个索引中时,优化器可以直接从该索引页拿到全部所需数据,无需再用主键去聚簇索引里查一次完整记录——这就是“回表”。覆盖索引本质是让查询“止步于二级索引”,跳过回表开销。

常见误判:以为只要

WHERE
条件用了索引就算覆盖。其实必须
SELECT
列、
WHERE
列、
ORDER BY
列、
GROUP BY
列全部被同一索引包含才算真正覆盖。

如何确认一个查询是否走覆盖索引

执行

EXPLAIN
,重点看两列:

type
ref
/
range
/
const
等合理类型(不是
ALL
Extra
字段不含
Using filesort
Using temporary
,且明确出现
Using index
(注意不是
Using index condition

示例:

EXPLAIN SELECT user_id, status FROM orders WHERE shop_id = 123;

orders
表有联合索引
(shop_id, user_id, status)
,则该语句会显示
Extra: Using index
;但如果只查
SELECT *
或多了
created_at
这种未包含字段,就会变成
Using where; Using index
(回表发生)。

设计覆盖索引的实操要点

覆盖索引不是越多越好,要结合高频查询模式权衡:

WHERE
最左列放在索引最前(符合最左前缀原则)
SELECT
中所有需返回的列追加在右侧(顺序无关,但影响索引体积)
避免把大字段(如
TEXT
、长
VARCHAR
)放进索引,否则索引页膨胀,反而拖慢扫描速度
如果查询常带
ORDER BY a,b
,且
a
已在
WHERE
条件中,则把
a,b
放进索引可避免排序(
Using index
同时覆盖查找+排序)

反例:为

SELECT name FROM users WHERE age > 25
建索引
(age, name, email)
——
email
完全无用,徒增索引大小和维护成本。

覆盖索引失效的典型场景

即使建了看似完整的索引,以下情况仍会强制回表:

SELECT
中用了函数或表达式:
SELECT UPPER(name) FROM t WHERE id = 1
→ 即使有
(id, name)
索引,
UPPER()
导致无法直接取索引值
隐式类型转换:
WHERE phone = 13800138000
(phone 是
VARCHAR
),触发全索引扫描+回表
使用
!=
NOT IN
IS NULL
等无法利用索引有序性的条件,优化器可能放弃覆盖路径
索引列上用了
LIKE '%xxx'
,导致无法走索引查找,自然也无法覆盖

真正起作用的永远是执行计划里的

Using index
,而不是“我以为它应该能覆盖”。每次加索引后,务必用真实查询
EXPLAIN
验证。

相关推荐