MySQL 慢查询排查案例复盘
真实线上案例:系统卡顿 → 慢查询定位 → EXPLAIN 分析 → 索引优化 → 性能对比验证
问题背景
晚上 8 点接到告警,线上系统响应变慢,页面平均加载时间从 200ms 飙升到 5s+。监控显示数据库 CPU 从 20% 飙升到 85%。
第一步:定位慢查询
-- 开启慢查询日志(如果还没开)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
-- 查看当前正在执行的慢查询
SELECT * FROM information_schema.PROCESSLIST
WHERE COMMAND != 'Sleep' AND TIME > 2
ORDER BY TIME DESC;
当场抓到一条耗时 4.7s 的查询:
SELECT o.order_id, o.total_amount, o.status, u.username, u.phone
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE o.created_at > '2025-01-01'
ORDER BY o.total_amount DESC
LIMIT 20;
第二步:EXPLAIN 分析
EXPLAIN SELECT o.order_id, o.total_amount, o.status,
u.username, u.phone
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE o.created_at > '2025-01-01'
ORDER BY o.total_amount DESC
LIMIT 20;
结果分析:
| 字段 | 值 | 结论 |
|---|---|---|
o.type |
ALL | orders 全表扫描 |
o.rows |
980000 | 扫描了 98 万行 |
o.Extra |
Using where; Using filesort | 文件排序(未用索引排序) |
u.type |
eq_ref | users 表索引正常 |
u.rows |
1 | 每次关联扫描 1 行 |
问题确认:
orders表全表扫描 —created_at没有索引Using filesort—total_amount也没有索引,排序在磁盘上完成- 两张问题叠加:全表扫描 + 文件排序 = 灾难
第三步:加索引
-- 创建复合索引(覆盖 WHERE 条件和 ORDER BY)
ALTER TABLE orders ADD INDEX idx_create_amount (created_at, total_amount);
设计思路:
created_at放前面:满足 WHERE 范围查询total_amount放后面:满足 ORDER BY 排序,且因created_at过滤后有排序稳定性
第四步:效果验证
优化前(4.7s → 优化后 23ms):
# 优化前
# Query_time: 4.718782 Lock_time: 0.000162 Rows_sent: 20 Rows_examined: 980000
# 优化后
# Query_time: 0.023401 Lock_time: 0.000108 Rows_sent: 20 Rows_examined: 56218
| 指标 | 优化前 | 优化后 | 提升倍数 |
|---|---|---|---|
| 查询耗时 | 4.72s | 23ms | 205x |
| 扫描行数 | 980,000 | 56,218 | 17x |
| 数据库 CPU | 85% | 30% | 恢复正常 |
复盘要点
- 索引缺失是慢查询最常见的根因 —
created_at没有索引导致全表扫描 - 复合索引设计 — WHERE + ORDER BY 的列可以放在同一个索引中,避免 filesort
- EXPLAIN 必须成为排查习惯 —
type=ALL+Extra=Using filesort是两个最危险的红旗信号 - 分步验证 — 加索引前记录 baseline,加索引后对比执行计划,确认走索引
关联页面
| 页面 | 关联点 |
|---|---|
| mysql-performance-config | MySQL 性能调优总纲(慢查询/索引失效/深度分页/锁分析/配置模板) |
| mysql-replication-guide | MySQL 主从复制,慢查询对从库延迟的影响 |
| fullstack-performance-troubleshooting | 全栈排障方法论(Nginx→应用→数据库→服务器) |