mysql如何通过explain优化索引_mysql实战调优

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

EXPLAIN 输出里哪些字段最关键

EXPLAIN
结果,别只盯着
type
key
,真正决定性能的是
rows
filtered
的乘积——它近似代表 MySQL 实际扫描的行数。比如
rows=10000
filtered=10.00
(即 10%),意味着大概要检查 1000 行;但如果
filtered
是 0.1,那就得筛 10 行——差别巨大。

常见误判:看到

type=ref
就以为没问题,但若
rows
是百万级,说明索引没过滤掉多少数据,可能需要更选择性的索引或调整查询条件顺序。

key
为空?说明没走索引,先查
possible_keys
是否有候选,再确认 WHERE 条件是否符合最左前缀原则
Extra
出现
Using filesort
Using temporary
?大概率是排序/分组字段没被索引覆盖,需加联合索引
key_len
比预期小?比如定义了
INDEX(a,b,c)
,但
key_len
只显示 a 的长度,说明 b/c 没参与索引查找——通常因为 WHERE 中跳过了 b

联合索引怎么建才不浪费

联合索引不是字段堆砌,而是按「过滤性 + 查询频率 + 排序需求」排优先级。高区分度字段(如

user_id
)放前面,低区分度(如
status
)放后面;等值查询字段优先于范围查询字段(
WHERE a=1 AND b>100
→ 索引应为
(a,b)
,不能是
(b,a)
)。

容易踩的坑:

ORDER BY
字段必须紧接在 WHERE 等值字段之后,才能复用索引避免
Using filesort
。例如
WHERE category=2 AND is_valid=1 ORDER BY created_at DESC
,索引应为
(category, is_valid, created_at)
,而不是
(category, created_at, is_valid)

字符串字段加索引要控制长度:
VARCHAR(255)
不必全字段索引,
INDEX(title(191))
足够应对多数场景,还能节省 B+ 树层级
避免冗余索引:
INDEX(a)
INDEX(a,b)
同时存在时,前者基本无效,删掉
日期范围慎用:对
created_at > '2023-01-01'
单独建索引效果差,应结合高选择性字段组成联合索引

为什么 force index 有时反而更慢

FORCE INDEX
是告诉优化器“必须用这个索引”,但它不解决底层数据分布问题。如果该索引的
rows
预估远高于实际最优路径(比如统计信息过期),强制使用只会让执行计划更糟。

典型场景:表刚导入大量数据,但没

ANALYZE TABLE
,优化器仍按旧统计估算,此时
FORCE INDEX
可能锁死在低效路径上;或者索引本身覆盖不全,导致回表次数爆炸(
Extra
出现大量
Using where
+ 高
rows
)。

先跑
ANALYZE TABLE table_name
更新统计信息,再看
EXPLAIN
是否自动选对索引
SELECT * FROM table_name WHERE ...
对比
SELECT id FROM table_name WHERE ...
EXPLAIN
,判断是否因回表成本过高导致优化器弃用索引
FORCE INDEX
仅用于临时验证,上线前务必还原并靠索引设计和统计信息解决根本问题

慢查询里隐式类型转换怎么揪出来

当 WHERE 条件字段是

VARCHAR
,但传入数字(如
WHERE phone = 13812345678
),MySQL 会把整列转成数字比较,导致索引失效——
EXPLAIN
key
为空,
type
变成
ALL

这种问题在 ORM 自动生成 SQL 时特别隐蔽,比如 MyBatis 把 Java

Long
直接拼进字符串条件,或前端传参没加引号。检查方法很简单:把慢查询日志里的 SQL 拿到 MySQL 客户端执行,再
EXPLAIN
,重点看
type
key
是否异常。

统一用字符串传参:
WHERE phone = '13812345678'
建表时尽量让字段类型和业务输入一致,避免
INT
存手机号(后期想加前缀就崩溃)
SHOW WARNINGS
查看隐式转换警告,MySQL 8.0+ 会在警告中明确提示 “Cast to type …”

实际调优中,90% 的索引问题出在联合索引字段顺序、字符串字段未截取长度、以及隐式类型转换这三处。只要每次写完查询都扫一眼

EXPLAIN
rows
Extra
,就能避开大部分线上性能雷区。

相关推荐