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,就能避开大部分线上性能雷区。
