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 索引失效的十大原因与优化方案》

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

相关推荐
写点啥呢2 小时前
Android Studio 多语言助手插件:让多语言管理变得简单高效
android·ai·ai编程·多语言
CodeLongBear3 小时前
MySQL进阶学习笔记:从单表查询到多表关联的深度解析(万字详解)
笔记·学习·mysql
运维_攻城狮3 小时前
openeuler-24.3欧拉系统mysql开机自启报错
linux·mysql
池塘水悠悠3 小时前
MySQL8.4小版本升级(通过data复制办法)
adb
魏 无羡3 小时前
windows 安装mysql(多个版本同时安装)
windows·mysql·adb
喵行星3 小时前
MySQL XtraBackup 使用文档(全量 + 增量备份与恢复)
数据库·mysql·adb
泥嚎泥嚎4 小时前
【Android】给App添加启动画面——SplashScreen
android·java
全栈派森4 小时前
初见 Dart:这门新语言如何让你的 App「动」起来?
android·flutter·ios
dessler4 小时前
MYSQL-外键(Foreign Key)
linux·运维·mysql