# MySQL慢查询排查全流程,从发现问题到优化SQL

0 阅读3分钟

线上系统越来越慢,用户反馈"页面转圈圈"。一看数据库,一条SQL要跑好几秒。

怎么定位慢SQL?怎么分析原因?怎么优化?今天把完整排查流程走一遍。

第一步:找到慢SQL

方法一:开启慢查询日志

-- 查看当前配置
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';

-- 动态开启(不需要重启)
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;  -- 超过1秒的记录
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
-- 查看慢查询日志内容
SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 20;

方法二:SHOW PROCESSLIST实时看

SHOW FULL PROCESSLIST;
列关注点
Time执行时间,越大越可疑
State当前状态,"Sending data"通常在计算
Info正在执行的SQL

第二步:分析SQL执行计划

EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 'PAID';
列含义关注点
type访问类型system > const > ref > range > index > ALL
key使用的索引NULL说明没走索引
rows预估扫描行数越小越好
Extra额外信息Using filesort、Using temporary要警惕

type列的关键值:

type说明性能
system表只有一行最快
const主键/唯一索引等值查询极快
ref普通索引等值查询快
range索引范围扫描还行
index全索引扫描慢
ALL全表扫描最慢

第三步:常见优化手段

优化一:加索引

-- 原始SQL:全表扫描
SELECT * FROM orders WHERE user_id = 123 AND status = 'PAID';

-- 加联合索引
ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);

-- 优化后:type=ref,rows大幅减少

**优化二:避免SELECT ***

-- 不好:取回所有列
SELECT * FROM orders WHERE user_id = 123;

-- 好:只取需要的列
SELECT id, order_no, amount, create_time
FROM orders
WHERE user_id = 123;

优化三:分页优化

-- 深分页很慢
SELECT * FROM orders ORDER BY id LIMIT 1000000, 20;
-- 实际要扫描100万+20行

-- 优化:用上次最大的id做游标
SELECT * FROM orders
WHERE id > 上次最后一条的id
ORDER BY id
LIMIT 20;

-- 或者子查询优化
SELECT * FROM orders
WHERE id >= (SELECT id FROM orders ORDER BY id LIMIT 1000000, 1)
ORDER BY id
LIMIT 20;

优化四:避免索引失效

-- 不好:对索引列做运算
SELECT * FROM orders WHERE YEAR(create_time) = 2026;

-- 好:改成范围查询
SELECT * FROM orders
WHERE create_time >= '2026-01-01'
  AND create_time < '2027-01-01';

-- 不好:隐式类型转换
SELECT * FROM users WHERE phone = 13800138000; -- phone是varchar

-- 好:类型匹配
SELECT * FROM users WHERE phone = '13800138000';

第四步:查看实际执行耗时

-- 开启profiling
SET profiling = 1;

-- 执行你的SQL
SELECT * FROM orders WHERE user_id = 123;

-- 查看耗时
SHOW PROFILES;
+----------+------------+----------------------------------------------+
| Query_ID | Duration   | Query                                        |
+----------+------------+----------------------------------------------+
|        1 | 0.00234567 | SELECT * FROM orders WHERE user_id = 123     |
+----------+------------+----------------------------------------------+

实战案例

案例:一个报表查询从8秒优化到0.2秒

-- 原始SQL(8秒)
SELECT u.name, COUNT(o.id) as order_count, SUM(o.amount) as total
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.create_time BETWEEN '2026-01-01' AND '2026-12-31'
GROUP BY u.id, u.name
ORDER BY total DESC
LIMIT 100;

-- 问题分析:
-- 1. orders表缺少create_time索引
-- 2. GROUP BY没有覆盖索引
-- 3. LEFT JOIN其实不需要(WHERE条件已经过滤了)

-- 优化后
ALTER TABLE orders ADD INDEX idx_time_user (create_time, user_id);

SELECT u.name, t.order_count, t.total
FROM (
    SELECT user_id, COUNT(*) as order_count, SUM(amount) as total
    FROM orders
    WHERE create_time >= '2026-01-01' AND create_time < '2027-01-01'
    GROUP BY user_id
    ORDER BY total DESC
    LIMIT 100
) t
JOIN users u ON t.user_id = u.id;

-- 优化后0.2秒

优化思路:先在子查询中完成聚合(减少JOIN的数据量),再关联用户表取名字。


慢查询排查是后端开发的基本功。流程就是:找到慢SQL → EXPLAIN分析 → 针对性优化 → 验证效果。

建议把自己项目的慢查询日志打开看看,可能有很多惊喜。