为什么 EXPLAIN
看不出全表扫描但实际很慢?
因为
EXPLAIN只展示预估执行计划,不反映真实数据分布、锁等待、磁盘 I/O 延迟或临时表溢出。比如
WHERE条件命中索引,但返回 50 万行结果集,MySQL 仍可能因排序/分组被迫创建内部临时表并写入磁盘。 务必配合
SHOW PROFILE FOR QUERY N查看各阶段耗时(尤其是
Copying to tmp table、
Sorting result) 用
SELECT ... INTO OUTFILE或流式读取应用层分页,避免一次性拉取超大结果集 检查
tmp_table_size和
max_heap_table_size是否过小,导致内存临时表退化为磁盘临时表
ORDER BY
+ LIMIT
为何有时不走索引?
当
ORDER BY字段和
WHERE条件字段不在同一复合索引中,或索引顺序不匹配排序方向(如索引是
(a, b),而查询写
ORDER BY b DESC),MySQL 可能放弃索引排序,改用文件排序(
Using filesort)。 优先建立覆盖排序需求的复合索引,例如
WHERE status=1 ORDER BY created_at DESC→ 建索引
(status, created_at)避免在
ORDER BY中使用函数或表达式,如
ORDER BY DATE(created_at)会失效
LIMIT很小(如
LIMIT 10)但偏移量极大(如
OFFSET 100000)时,即使走索引也要跳过前 10 万行 —— 改用游标分页(
WHERE id > last_seen_id ORDER BY id LIMIT 10)
哪些 JOIN
写法会让优化器“放弃治疗”?
MySQL 优化器对多表关联的代价估算较保守,遇到子查询、
OR条件、函数条件或非等值连接时,常误判驱动表顺序,甚至退化为嵌套循环暴力匹配。 把
IN (SELECT ...)改成
JOIN或
EXISTS;
NOT IN必须警惕 NULL 导致逻辑错误,优先用
NOT EXISTS避免
ON a.x = b.y + 1这类表达式条件,它无法利用
b.y上的索引 显式用
STRAIGHT_JOIN强制连接顺序(仅当确认优化器选错且已验证效果) 大表
JOIN小表时,确保小表有高效过滤条件,并在小表上建好关联字段索引
线上慢查询突然变快,是不是优化成功了?
未必。可能是缓存生效(Query Cache 已废弃,但 InnoDB Buffer Pool 缓存了热点页)、统计信息未更新导致优化器选了更“侥幸”的路径,或并发压力暂时下降。这类“伪优化”会在流量高峰或数据量增长后重现。
执行ANALYZE TABLE更新统计信息,再看
EXPLAIN是否稳定 用
SELECT SLEEP(1)模拟低并发,对比高并发下
SHOW PROCESSLIST中的
State(如频繁出现
Waiting for table metadata lock要查长事务) 开启慢查询日志并设置
long_query_time = 0抽样捕获所有查询,结合
pt-query-digest分析分布规律
真正可靠的优化,是让最差情况下的 P99 响应时间也落在预期范围内,而不是依赖缓存或运气。索引设计、数据类型精简、避免隐式转换,这些底层约束比任何 SQL 技巧都重要。
