数据库 MySQL排查

一条慢查询的三天求生记

运营报表接口从 2 秒慢到 20 秒,前后拖了三天才彻底解决。复盘一下,过程中的三次误判比结论更有价值。

第一天,想当然加索引。EXPLAIN 一看 type=ALL 全表扫描, WHERE 里有 status 和 create_time,顺手建了复合索引。EXPLAIN 再跑——还是全表。当时没细想为什么,只觉得"索引没生效"。

第二天,怀疑统计信息。重新 ANALYZE TABLE,更新了优化器统计,还是慢。把 EXPLAIN 加上 FORMAT=JSON 仔细看,才注意到 rows 估计值和实际扫描行数差了一个数量级,而且 Extra 里写着 Using where; Using filesort。真正的线索是:查询里 WHERE DATE(create_time) = '2026-07-14'——索引列套了函数。B+ 树里存的是原始值,套了函数就只能逐行算。

-- 改前:索引失效
SELECT ... WHERE DATE(create_time) = '2026-07-14';
-- 改后:范围条件,索引正常工作
SELECT ... WHERE create_time >= '2026-07-14' AND create_time < '2026-07-15';
处理流程示意
处理流程示意

第三天,做成了规范。改完这一条,20 秒降到 300 毫秒。顺手把代码库里 DATE(、YEAR( 出现在 WHERE 的地方全扫了一遍,还有两处潜伏的同类问题。

三天里真正的教训只有一个:EXPLAIN 不是跑一遍看 type 就完事,Extra 列和 rows 估计值才是细节所在。以及,"加了索引就完事"的直觉,十次里总有一次要还的。