MySQL 慢查询优化:从定位、分析到索引调优的完整流程

文章目录


《MySQL 慢查询优化:从定位、分析到索引调优的完整流程》


一、前言:为什么会有慢查询

大家好,我是程序员卷卷狗。

在业务量快速增长的系统中,数据库性能问题往往出现在:

  • 表数据量过大;
  • SQL 没有走索引;
  • 索引设计不合理;
  • 统计信息失真导致优化器误判。

慢查询优化的本质:

找出执行慢的 SQL,分析原因,减少不必要的扫描与排序。


二、第一步:开启慢查询日志

MySQL 内置了慢查询日志(slow_query_log),

用于记录执行时间超过阈值的 SQL。

(1)开启方式
sql 复制代码
SET GLOBAL slow_query_log = 1;
SET GLOBAL long_query_time = 1;  -- 超过1秒记录
SET GLOBAL log_output = 'TABLE'; -- 输出到mysql.slow_log表
(2)查看慢查询数量
sql 复制代码
SHOW GLOBAL STATUS LIKE 'Slow_queries';
(3)查看慢日志内容
sql 复制代码
SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 5;

建议:

  • 线上可设置 long_query_time=0.5
  • 日志开启后结合定期分析工具(如 pt-query-digest)。

三、第二步:分析慢查询日志

使用官方或 Percona 工具进行日志聚合分析。

示例命令:
bash 复制代码
pt-query-digest /var/lib/mysql/slow.log > slow_report.txt
输出指标:
指标 含义
Count SQL 执行次数
Exec time 总耗时
Lock time 锁等待时间
Rows sent 返回行数
Rows examine 扫描行数
Query ID SQL 模板标识

通过分析聚合结果,找出:

  • 执行最慢的 SQL;
  • 执行最频繁但扫描量大的 SQL;
  • 存在重复逻辑或未命中索引的语句。

四、第三步:Explain 执行计划分析

拿到慢 SQL 后,先用 EXPLAIN 查看执行计划。

示例:

sql 复制代码
EXPLAIN SELECT * FROM orders WHERE status='PAID' AND create_time > '2025-01-01';

输出:

复制代码
type: ALL
rows: 500000
Extra: Using where

分析:

  • type=ALL → 全表扫描;
  • rows=50 万 → 扫描过多;
  • Extra=Using where → 没有使用索引。

解决方案:

sql 复制代码
CREATE INDEX idx_status_time (status, create_time);

再执行:

复制代码
type: range
key: idx_status_time
rows: 1200
Extra: Using index condition

性能显著提升。


五、第四步:确认索引命中逻辑

优化索引时要遵守"查询模式匹配原则"。

场景 优化前 优化后
仅范围条件 WHERE age > 18 建单列索引 idx_age
组合条件 WHERE status='PAID' AND user_id=100 建联合索引 (status, user_id)
排序字段 ORDER BY create_time 为排序列建索引
分组查询 GROUP BY status 联合索引中包含分组列

索引优化的核心目标:减少 I/O,避免 filesort 与临时表。


六、第五步:使用 SQL Profile 与实际耗时验证

可以使用 SHOW PROFILE 命令查看 SQL 各阶段耗时。

示例:
sql 复制代码
SET profiling = 1;
SELECT * FROM orders WHERE user_id=10000;
SHOW PROFILES;
SHOW PROFILE FOR QUERY 1;

输出:

复制代码
| Stage | Duration | Detail |
|--------|-----------|--------|
| parsing sql | 0.0001 | SQL解析 |
| optimizing | 0.0023 | 优化器分析 |
| executing | 0.1105 | 扫描行/返回结果 |

若执行阶段耗时占比过高,说明索引或过滤逻辑仍可优化。


七、第六步:改写 SQL 与缓存优化

① 避免 SELECT *

只查询必要字段,减少 I/O 与网络传输。

sql 复制代码
SELECT id, name FROM user WHERE id=1;
② 用 EXISTS 替代 IN
sql 复制代码
-- ❌ 慢
SELECT * FROM user WHERE id IN (SELECT user_id FROM orders);
-- ✅ 快
SELECT * FROM user WHERE EXISTS (SELECT 1 FROM orders WHERE orders.user_id=user.id);
③ 合理使用 LIMIT

分页时配合索引字段:

sql 复制代码
SELECT * FROM orders WHERE id > ? ORDER BY id LIMIT 20;
④ 引入缓存层
  • Redis 缓存热点数据;
  • 使用 Query Cache(8.0 已废弃)或应用层缓存。

八、SQL 优化流程总结图

复制代码
┌────────────────────────────┐
│   1️⃣ 开启慢查询日志        │
├────────────────────────────┤
│   2️⃣ 分析日志找慢 SQL      │
├────────────────────────────┤
│   3️⃣ Explain 看执行计划    │
├────────────────────────────┤
│   4️⃣ 建索引 / 改写 SQL     │
├────────────────────────────┤
│   5️⃣ SHOW PROFILE 验证耗时  │
├────────────────────────────┤
│   6️⃣ 实测压测对比效果      │
└────────────────────────────┘

这套流程就是数据库调优工程师的日常操作路径。


九、面试高频问题与答题模板

问题 答案要点
Q1:如何开启慢查询日志? SET GLOBAL slow_query_log=1;
Q2:慢查询阈值怎么设置? long_query_time=1 表示超 1 秒记录。
Q3:慢查询如何分析? 使用 pt-query-digest 聚合分析。
Q4:Explain 重点看什么? type、key、rows、Extra。
Q5:如何判断 SQL 是否走索引? 看 type≠ALL 且 key 不为空。
Q6:如何降低 rows 数量? 优化索引设计,减少扫描。
Q7:慢 SQL 优化的一般步骤? 开日志 → 分析 → Explain → 索引优化 → 验证。

十、总结

慢查询优化并不是单点问题,而是一整套系统性流程。

一句话记住:

"先找出谁慢,再分析为什么慢,最后让它变快。"

掌握这套流程后,你不仅能在面试中答得漂亮,

还能在真实项目中定位瓶颈、精准调优。

下一篇(第 17 篇),我将写------
《MySQL 索引失效的十大原因与优化方案》

总结最常见的索引失效场景(函数、隐式转换、范围中断等)及优化思路。

相关推荐
阿巴斯甜19 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker20 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952721 小时前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android