mysql索引优化中的查询重写与查询计划分析

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

为什么
EXPLAIN
显示
type=ALL
却没走索引

这通常不是索引没建,而是查询写法触发了隐式类型转换或函数包裹,导致索引失效。比如

WHERE id = '123'
id
INT
),MySQL 会把字符串转成数字再比对,但优化器可能放弃索引;更常见的是
WHERE DATE(create_time) = '2024-01-01'
——
DATE()
函数让
create_time
索引完全失效。

实操建议:

SHOW WARNINGS
查看优化器重写后的语句,确认是否被改写成不可索引的形式
避免在索引列上使用函数、表达式、
LIKE '%xxx'
开头的模糊匹配
字符串比较时确保类型一致:
WHERE status = 1
status
TINYINT
),别写成
'1'
时间范围查询优先用
BETWEEN
或闭区间:
WHERE create_time >= '2024-01-01' AND create_time ,而非 <code>DATE(create_time) = ...

EXPLAIN FORMAT=JSON
比传统
EXPLAIN
多看出什么

传统

EXPLAIN
只给粗粒度信息,比如
key
用了哪个索引、
rows
预估扫描行数;而
FORMAT=JSON
能暴露优化器真实决策路径,特别是是否发生索引合并(
index_merge
)、是否使用了索引条件下推(ICP)、是否因
ORDER BY
需要临时文件排序等。

关键字段注意:

attached_condition
:显示下推到存储引擎层的过滤条件,ICP 生效时这里会变短
using_index
:为
True
表示覆盖索引,无需回表
using_filesort
using_temporary
:说明排序/分组没走索引,性能瓶颈大概率在这
filtered
值过低(如
10.00
)说明虽然走了索引,但索引选择性差,大量无效行被拉到 server 层过滤

哪些
ORDER BY
场景必须重写才能走索引

只有当

ORDER BY
字段顺序与索引最左前缀严格一致、且无混合
ASC/DESC
时,才可能利用索引排序。例如有联合索引
(a, b, c)
,则
ORDER BY a, b
可走索引,但
ORDER BY b, c
不行;
ORDER BY a DESC, b ASC
在 MySQL 8.0 之前也基本失效。

常见可重写方式:

ORDER BY RAND()
改为应用层随机取 ID 后查,或用
OFFSET
+
LIMIT
分页模拟(但大数据量仍慢)
ORDER BY func(x)
(如
UPPER(name)
)无法走索引,考虑加函数索引(MySQL 8.0+):
CREATE INDEX idx_name_upper ON t ((UPPER(name)))
多表
JOIN
ORDER BY
非驱动表字段,往往触发
using_filesort
,优先调整
JOIN
顺序或加覆盖索引包含排序字段

查询重写时容易忽略的统计信息偏差问题

即使重写后语法“看起来能走索引”,如果表的统计信息陈旧(比如刚批量导入百万数据但没

ANALYZE TABLE
),优化器仍可能基于错误的行数估算选择全表扫描。尤其在
WHERE
条件区分度高但优化器误判为低时,
EXPLAIN
会显示
type=range
却实际扫描巨量行。

验证和修复步骤:

查统计信息更新时间:
SELECT UPDATE_TIME FROM information_schema.tables WHERE TABLE_SCHEMA = 'db' AND TABLE_NAME = 't'
强制刷新:
ANALYZE TABLE t
(线上慎用,会锁表;MySQL 5.7+ 可设
innodb_stats_auto_recalc = ON
必要时用
FORCE INDEX
临时绕过错误选择,但只是兜底,不能替代根本优化
注意
innodb_stats_persistent
是否开启,关闭时重启后统计信息丢失,可能导致计划突变

真正难的不是写出能走索引的 SQL,而是理解优化器在特定数据分布、统计精度、版本特性下的真实行为边界。一次

EXPLAIN
看不出问题,得结合
FORMAT=JSON
、执行时间、
SHOW PROFILE
和实际
slow log
对齐才可靠。

相关推荐