【MySQL百日打怪升级第23天】慢查询日志分析 —— 网站慢了,第一步该看什么?

简介: 本文是DBA老兵分享的慢查询日志分析实战指南:从配置启用(slow_query_log、long_query_time等)、日志解读(重点看Rows_examined/Rows_sent比值)、工具选用(pt-query-digest为首选)到完整排查流程,强调“先看日志再动手”,避免盲目重启加缓存。含避坑提示与面试考点。

慢查询日志分析 —— 网站慢了,第一步该看什么?


大家好,我是一名拥有10年以上经验的DBA老兵。

做这个系列,源于一个朴素的愿望:把踩过的坑、总结的经验系统化输出,希望能帮到刚入行或想进阶的兄弟们。

让我们开始今天的第23天内容。


前两天聊了死锁,今天聊一个更常见的场景——网站突然变慢了。

这时候大多数人的反应是:重启、加缓存、加机器。但一个合格的 DBA 第一件事是——查慢查询日志。

慢查询日志是 MySQL 给你留的"案发现场"。谁慢了?慢在哪里?花了多久?全记在上面。不看日志就动手,属于闭着眼睛修车。


配置:先确保日志开着

很多服务器的慢查询日志默认是关的。第一步确认它开着:

SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';

关键参数:

参数 说明 推荐值
slow_query_log 是否开启 ON
slow_query_log_file 日志文件路径 默认在 data 目录
long_query_time 超过多少秒算慢 线上 12,分析时临时设 0
log_queries_not_using_indexes 没走索引的查询也记录 非生产和低负载:ON;高负载生产:酌情开
log_slow_admin_statements 慢管理语句也记录(DDL等) 建议 ON

长期建议 long_query_time=1,意思是超过 1 秒就算慢。有些团队设 0.5,也可以,但要看你服务器的负载——如果日志量太大,设太短反而淹没了真正的慢查询。

log_queries_not_using_indexes 也要注意代价:如果业务代码里有大量没走索引的小查询,这个开关一开会让你的日志文件瞬间暴涨,IO 开销也跟着上来。

⚠️ 注意:在高并发生产环境开启 log_queries_not_using_indexes 前,先评估你的慢查询量。如果业务代码里大量查询都没走索引(哪怕单次只要几十毫秒),这个开关会让日志量暴增 10-100 倍,IO 也会跟着涨。建议先在从库或低峰期试开,观察一段时间再决定是否长期开启。

一句话总结:非生产环境必开,低负载生产建议开,高并发生产开在从库上或只在排查时临时开。

临时调成 0 抓全量是个好技巧:

SET GLOBAL long_query_time = 0;
-- 跑一段时间抓完,记得改回来
SET GLOBAL long_query_time = 1;

但有个风险:如果你忘了改回来,日志会把磁盘撑爆。 建议要么设个定时任务自动恢复:

# 10 分钟后自动恢复
echo "SET GLOBAL long_query_time = 1;" | mysql -u root -p

要么不用 0,设一个极低但安全的值:

SET GLOBAL long_query_time = 0.01;  -- 10 毫秒,过滤掉真正的零耗时查询

注意 SET GLOBAL 对已有连接不生效,新连接才生效。

还有一件事容易被忽略:慢查询日志是同步写操作。 MySQL 每执行完一条符合条件的慢查询,必须把日志写进磁盘才继续往下走。所以日志 IO 本身也会影响性能——如果日志盘 IO 撑满了,查询也会被拖慢。

因此日志文件要定期切割。切割时慢日志和全日志一起做,用 move 方式切换,不要直接 truncate

# 凌晨低峰期,写个脚本统一切割
mv /var/log/mysql/slow.log /var/log/mysql/slow.log.$(date +%Y%m%d)
mv /var/log/mysql/general.log /var/log/mysql/general.log.$(date +%Y%m%d)
mysqladmin -u root -p flush-logs

为什么用 mv + flush-logs 而不是直接 truncate?

  • mv 只是改了文件名,MySQL 进程打开的文件描述符依然指向同一个 inode,所以在 flush 之前,日志会继续写入已被改名的旧文件,不会丢失。
  • 执行 flush-logs 后,MySQL 关闭旧文件句柄、创建新文件。切换过程有极短暂的阻塞窗口,但选在凌晨低峰期做,影响几乎可以忽略。
  • 千万不要 cat /dev/null > slow.log 或直接 rm 正在写的日志文件

logrotate 配好 postrotate 脚本也能自动完成切割。


读一条慢查询日志

一条典型的慢查询日志长这样:

# Time: 2026-05-18T10:30:15.123456Z
# User@Host: app_user[app_user] @ db01.example.com [10.0.1.100]  Id: 12345
# Query_time: 12.345678  Lock_time: 0.001234  Rows_sent: 10  Rows_examined: 500000
SET timestamp=1747559415;
SELECT o.id, o.amount, u.name
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 'pending'
ORDER BY o.created_at DESC
LIMIT 10;

每一列都有用:

字段 含义 重点关注
Query_time SQL 执行耗时(秒) 是否超过 long_query_time
Lock_time 锁等待时间 如果锁时间占比高,可能是锁竞争
Rows_sent 最终返回的行数 正常
Rows_examined 扫描了多少行 核心指标 —— Rows_examined 远大于 Rows_sent 说明扫描了大量数据

上面这个例子:扫描了 50 万行,只返回了 10 行。 50万/10 的比值过于离谱,一眼就可以判断——o.status 没有索引,或者有索引但选择性太差了。


工具:不要手动翻日志

日志文件大了以后(线上经常几 GB),不要 cat 看,用工具。

mysqldumpslow(MySQL 自带)

# 按平均查询时间排序,看前 10 条
mysqldumpslow -t 10 /var/log/mysql/slow.log

# 按执行次数排序
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log

自带工具的好处是哪里都有,不用额外安装。缺点是分析能力有限,只能做简单的聚合。

pt-query-digest(Percona Toolkit)

这是 DBA 的标配。如果只能装一个 MySQL 工具,我选这个。

# 分析慢查询日志,输出一份完整的报告
pt-query-digest /var/log/mysql/slow.log

# 输出到文件慢慢看
pt-query-digest /var/log/mysql/slow.log > slow_report.txt

pt-query-digest 会输出一份按"总耗时"排序的报告,告诉你哪些 SQL 消耗了最多的数据库时间。排名第一的那个,就是你要优化的头号目标。

举个例子,它的输出会告诉你:

# Rank Query ID           Response time    Calls  R/Call  V/M   Item
# ==== ================== ================ ====== ======= ===== ====
# 1    0xAB12CD34EF56...   1256.3456 45.2%  1234   1.0178  0.03  SELECT orders
# 2    0xCD78EF90AB12...    456.7890 16.4%   567   0.8056  0.10  SELECT users

第1条 SQL 占了 45% 的慢查询总时间。优化它就解决了一半问题。

如果需要自动化集成(比如定时分析、对接告警系统),pt-query-digest 支持 JSON 输出:

pt-query-digest --output json /var/log/mysql/slow.log > slow.json

JSON 格式按 query 类分组,每条包含 fingerprint、metrics(Query_time、Rows_examined 等)、样本 SQL。配合 jq 处理非常方便,比如提取总耗时排名前5的 SQL:

pt-query-digest --output json slow.log | jq '.classes[:5] | .[].sample'

也有 --output json-anon 模式,会去掉样本 SQL 中的具体数据,适合脱敏后传给第三方分析。


一个完整的分步流程

我处理线上慢查询的步骤:

第一步:确认到底有多慢

-- 看一眼当前正在跑的查询
SHOW FULL PROCESSLIST;
-- 看 Time 列:跑了多久了?

第二步:打开慢查询日志,确认是偶发还是持续

tail -100 /var/log/mysql/slow.log

第 2.5 步:看一眼系统负载(容易被跳过但很重要)

top -bn1 | head -5      # CPU 负载
iostat -x 1 3           # 磁盘 IO 是否打满
free -h                 # 内存是否吃紧

如果 CPU/IO 已经打满,慢查询可能只是"受害者"。先找到真正的资源杀手(通常也是没走索引的全表扫描),别揪着某条 SQL 死磕。

第三步:用 pt-query-digest 找到头号敌人

pt-query-digest /var/log/mysql/slow.log | head -80

第四步:对目标 SQL 做 EXPLAIN

EXPLAIN SELECT ...\G

第五步:做索引优化或 SQL 改写,验证效果


⚠️ 一个常见的误判:慢的不一定是它自己

慢查询日志告诉你"这条 SQL 花了 10 秒",但原因不一定在这条 SQL 本身

有种常见的场景:这条 SQL 单独拿出来跑只要 100 毫秒,但在线上就是 10 秒。为什么?因为当时系统里还有其他慢查询在吃资源——CPU 打满了、IO 带宽被占完了、内存被挤爆了。

这条 SQL 本身没有变慢,它是被挤慢的。就像是堵车——不是你的车有问题,是整个路口都堵死了。

怎么判断?

-- 看当时系统的负载状态(如果日志里有)
# Schema: db1  # 数据库名
# Query_time: 10.5  Lock_time: 0.001  # SQL 耗时 10 秒

-- 如果同时间段有大量其他慢查询,且这条 SQL 单独 EXPLAIN 走索引、
-- rows 扫描量很小——大概率是被"挤慢"的,不是它自己的锅

取出来单独跑一下就能验证:

# 把这条 SQL 粘出来,在数据库里跑一次
# 如果单独跑很快,回去查同时间段的系统负载

根因可能是:其他全表扫描的 SQL 吃光了 IO、某个定时任务占满了 CPU、备份脚本在跑。这种情况你的优化目标不是这条 SQL,而是把那个真正的"资源杀手"找出来

所以诊断慢查询时,别只盯着"最慢的"那一两条。结合 SHOW FULL PROCESSLIST 和系统负载(CPU、IO、内存)一起看,才能找到真正的根因。


慢查询日志分析用 AI 有个常见的误区——把整份日志文件丢给 AI。 如果日志几 MB 甚至几百 MB,Token 消耗是天文数字,AI 光读完就超时了。

正确姿势:先用传统工具做聚合,再把样本交给 AI 做深度分析。

正确流程

慢查询日志(.log)
       ↓
pt-query-digest --output json(聚合排序,输出 JSON)
       ↓
从 JSON 中提取排名靠前的 SQL 样本(结构化,Token 极省)
       ↓
用 AI 执行 EXPLAIN 分析索引使用情况
       ↓
结合索引优化知识给出改索引或改 SQL 的建议

实操:从 pt-query-digest JSON 到 AI 分析

用 JSON 格式输出,比文本报告更适合 AI 处理——结构清晰、没有无关字符、Token 省很多:

pt-query-digest --output json /var/log/mysql/slow.log > slow.json

然后用 jq 提取排名靠前的样本 SQL 和关键指标:

# 提取前 5 条慢查询的样本 SQL 和执行次数
jq '.classes[:5] | .[] | {sql: .sample, calls: .metrics.Query_time.cnt, total_time: .metrics.Query_time.sum}' slow.json

把这条命令的输出直接交给 AI:

帮我分析这条 SQL 为什么慢,建议创建什么索引:

SELECT o.id, o.amount, u.name
FROM orders o JOIN users u ON o.user_id = u.id
WHERE o.status = 'pending'
ORDER BY o.created_at DESC LIMIT 10;

先执行 EXPLAIN,结合输出分析索引使用情况。

如果 AI 工具支持 MCP 直连数据库(比如 OpenCode 连接了数据库 MCP),可以让 AI 直接跑 EXPLAIN

执行 EXPLAIN SELECT ...,看 type、key、rows、Extra 字段,判断有没有走索引,给出优化建议。

结合前面 Day 5-6 讲的索引原理和 Day 24 即将讲的 EXPLAIN 解读,AI 可以帮你把一条慢查询从"知道慢"推进到"知道为什么慢、怎么修"。


面试加餐

Q: long_query_time 设为 0 会有什么后果?

会记录所有查询。这在排查问题时很有用(临时开启),但生产环境不要长期设为 0。MySQL 写日志是有 IO 开销的,所有查询都记会导致日志疯长,甚至把磁盘写满。用完记得关。

Q: Rows_examined 很大但 Rows_sent 很小,一定有问题吗?

不绝对。SELECT COUNT(*) FROM big_table 即使有索引也扫描了全表,但 Rows_sent = 1,这是正常的。但如果是一个 OLTP 查询(比如按用户查订单),Rows_examined 远大于 Rows_sent,基本可以判断索引有问题。

Q: pt-query-digest 和 mysqldumpslow 哪个好?

pt-query-digest 强太多了。它不仅按总耗时排序,还能做指纹聚合(把 WHERE id = ? 参数化后归并)、按时间段分析趋势、输出各种维度的统计。mysqldumpslow 就是 MySQL 自带的"能看就行"的版本。pt-query-digest 是 DBA 工具箱里最常用的工具,没有之一。


总结

🎯 面试考点

  • 慢查询日志的配置slow_query_log=ONlong_query_timelog_queries_not_using_indexes
  • 日志关键字段:Query_time、Lock_time、Rows_examined、Rows_sent —— 核心看扫描/返回比值
  • 分析工具mysqldumpslow(自带基本版)、pt-query-digest(DBA 标配)
  • 排查流程:SHOW PROCESSLIST → 查慢日志 → pt-query-digest 聚合 → EXPLAIN 定位 → 加索引 / 改 SQL
  • Rows_examined >> Rows_sent 是索引缺失的典型信号

今天就试一下:登录你的数据库,跑这条 SQL:

SHOW VARIABLES LIKE 'slow_query%';

看一眼你的慢查询日志开了没有。如果没开——今天就把它打开。

SET GLOBAL slow_query_log = ON;
SET GLOBAL log_queries_not_using_indexes = ON;

下期预告:EXPLAIN 执行计划解读 —— 面试必问!


有问题欢迎评论区交流,明天见!

相关文章
|
Java API p3c
【Java基础】@Transactional(rollbackFor=Exception.class)的使用
@Transactional(rollbackFor=Exception.class)的使用
1124 0
【Java基础】@Transactional(rollbackFor=Exception.class)的使用
|
5天前
|
SQL Kubernetes Java
六款好用的 IDEA 插件,强烈推荐!!!不容错过
本文介绍IntelliJ IDEA高效开发必备配置与插件:①禁用启动自动打开项目、②配置Maven路径、③自定义类/枚举模板(含作者、日期注释)、④调整编辑器字体;推荐6大实用插件——GenerateAllSetter(一键生成带默认值的setter)、Save Actions X(保存时自动格式化/清理无用导入)、Mybatis X(Mapper-XML双向跳转)、Mybatis Log Free(免费打印完整SQL)、Spring Boot Assistant(YAML智能补全)、Kubernetes(K8s资源文件语法提示与模板生成)。
六款好用的 IDEA 插件,强烈推荐!!!不容错过
|
5天前
|
人工智能 定位技术 Go
从零搭建 Harness Engineering 框架 :Rule、Skill、Sub-Agent等工程落完整路径
Harness Engineering 是一套让AI在真实项目中稳定、可靠交付的工程系统,涵盖SPEC规范、Rule约束、Skill流程、Sub-Agent分工、Workflow编排、Script校验与MCP集成。它不追求模型更聪明,而是通过结构化机制消除随意性,实现可验证、可维护、可持续的AI协作开发。
397 1
从零搭建 Harness Engineering 框架 :Rule、Skill、Sub-Agent等工程落完整路径
|
5天前
|
算法 测试技术 PyTorch
在 AMD ROCm DSW 上部署 Qwen3.6-27B-FP8:vLLM、MTP 解码加速与小并发压测
本文记录一次在 ModelScope DSW AMD GPU 实例上完成的 Qwen3.6-27B-FP8 推理实践。实验重点不是单纯证明模型可以启动,而是围绕 vLLM ROCm 服务、Qwen MTP 投机解码、near-8K 长上下文正确性验证、FP8 KV cache 和小并发 serving 压测,整理一套可复现、可复查、可继续扩展的 AMD GPU 大模型推理 baseline。
406 0
|
5天前
|
存储 人工智能 运维
千亿级 AI 搜索的效能实战:从混合检索到 Agentic RAG 的三年实战
本文为2026 Elastic中国大会演讲实录,直击千亿级AI搜索三大挑战:搜索融合(关键词+向量+稀疏检索原生一体)、极致效能(冷热分层、硬件降级、自研FalconSeek引擎)与Agentic RAG演进(结构化知识图谱+智能体自主推理),揭示企业级AI搜索从“能用”到“好用”再到“自进化”的实战路径。
286 8
|
5天前
|
缓存 前端开发 安全
ReAct推理链的工程化实现与最佳实践
本文介绍向量空间JBoltAI平台基于Spring Boot 3.x与Java 21实现的企业级ReAct推理链架构,涵盖分层设计、模板方法、Function Calling驱动、并发安全机制及推理可视化等核心实践,助力LLM能力可靠落地。
|
5天前
|
算法 NoSQL Java
Java在分布式ID生成器(雪花算法)中的实现与优化
在分布式系统中,需要全局唯一、趋势递增、高性能的ID(如订单号、消息ID)。数据库自增ID在分库分表后不再唯一;UUID无序且过长,影响索引性能。
222 1
|
5天前
|
存储 算法 C++
C++在高性能内存池(MemoryPool)设计与实现
频繁调用new/delete或malloc/free会造成性能损耗(系统调用、锁竞争、内存碎片)。
124 3
|
5天前
|
人工智能
AI工作流实践:如何降低内容运营中的接力成本?
AI工作流不替代人决策,而是打通内容运营全链路断点:需求沉淀、资料检索、初稿生成、审核留痕、发布记录、数据复盘。通过结构化任务+知识库+智能体协同,将反复查找、转述、遗漏的“接力成本”大幅降低,让人专注事实判断与质量把关。(239字)