MySQL 慢查询排查与索引优化实战

MySQL 慢查询排查与索引优化实战

某天凌晨,监控告警说订单详情接口 P99 从 80ms 飙到 2s。这类「偶发慢」十有八九是慢 SQL 在作祟。下面是我常用的排查链路。

第一步:打开慢查询日志

-- 查看当前慢查询阈值(秒)
SHOW VARIABLES LIKE 'long_query_time';

-- 会话级临时开启(生产建议用 pt-query-digest 离线分析)
SET long_query_time = 0.5;
SET slow_query_log = ON;

拿到慢日志后,重点关注 Rows_examined 远大于 Rows_sent 的语句——它往往在「全表扫描」。

第二步:用 EXPLAIN 看执行计划

EXPLAIN SELECT * FROM orders
WHERE user_id = 1024 AND status = 'PAID'
ORDER BY created_at DESC
LIMIT 20;

关键看这几列:

字段 健康信号 危险信号
type ref / range / const ALL(全表扫描)
key 命中了预期索引 NULL
rows 接近实际返回行数 远大于预期
Extra Using index Using filesort / Using temporary

上面这条 SQL 出现了 Using filesort,说明排序没有走索引。

第三步:建立合理的联合索引

MySQL 联合索引遵循最左前缀原则(user_id, status, created_at) 能同时加速 user_iduser_id + status 的过滤,且 created_at 在末尾可避免文件排序。

ALTER TABLE orders
ADD INDEX idx_user_status_created (user_id, status, created_at);

建立后再次 EXPLAINtype 变为 refExtra 中的 Using filesort 消失,P99 回到 90ms 左右。

几个容易踩的坑

  • 隐式类型转换WHERE phone = 13800000000phonevarchar,索引会失效;
  • 索引列上做函数运算WHERE DATE(created_at) = '2025-05-20' 同样无法走索引;
  • 过度索引:写多读少的表,索引会降低写入性能并占用空间。

经验法则:先用 EXPLAIN 量化问题,再针对性加索引,切忌「想到的列都加上」。

小结

排查慢 SQL 的标准动作就是 慢日志定位 → EXPLAIN 分析 → 最左前缀建索引 → 复测验证。把这几步固化成 checklist,大多数性能问题都能在半小时内定位。