explain仅显示预估执行计划,无法反映真实数据分布、锁等待、i/o延迟或临时表溢出;需结合show profile、合理索引、游标分页及统计信息更新综合诊断。

为什么 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 技巧都重要。











