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_id 与 user_id + status 的过滤,且 created_at 在末尾可避免文件排序。
ALTER TABLE orders
ADD INDEX idx_user_status_created (user_id, status, created_at);
建立后再次 EXPLAIN,type 变为 ref,Extra 中的 Using filesort 消失,P99 回到 90ms 左右。
几个容易踩的坑
- 隐式类型转换:
WHERE phone = 13800000000而phone是varchar,索引会失效; - 索引列上做函数运算:
WHERE DATE(created_at) = '2025-05-20'同样无法走索引; - 过度索引:写多读少的表,索引会降低写入性能并占用空间。
经验法则:先用 EXPLAIN 量化问题,再针对性加索引,切忌「想到的列都加上」。
小结
排查慢 SQL 的标准动作就是 慢日志定位 → EXPLAIN 分析 → 最左前缀建索引 → 复测验证。把这几步固化成 checklist,大多数性能问题都能在半小时内定位。