4.2 慢查询⽇志

慢查询日志是 MySQL 中一项至关重要的诊断工具,它专门记录执行时间超过指定阈值的 SQL 语句。通过分析这些"慢 SQL",你可以精准定位性能瓶颈,从而进行索引优化、SQL 重写或架构调整。


⚙️ 一、慢查询日志的核心配置

要使用慢查询日志,首先需要理解几个关键的系统变量。

参数 说明 默认值 / 推荐设置
slow_query_log 开关,设为 ON 启用 OFF,生产环境建议 ON
slow_query_log_file 日志文件路径 host_name-slow.log,通常指定为 /var/log/mysql/slow.log
long_query_time 慢查询阈值(秒),执行时间超过此值的语句会被记录 10 (秒),根据业务调整,OLTP 系统可设 0.1~1
log_queries_not_using_indexes 是否记录未使用索引的查询(即使执行很快) OFF,开启后可能产生大量日志,谨慎使用
min_examined_row_limit 扫描行数超过此值的查询才会被记录 0,可设为 1000 等,过滤掉扫极少行但偶发慢的查询
log_slow_admin_statements 是否记录 DDL 语句(如 ALTER TABLE OFF,根据需要开启
log_slow_replica_statements (8.0.26+) 从库是否记录复制过来的慢 SQL(替换旧变量 log_slow_slave_statements OFF
log_slow_extra (MySQL 8.0.14+) 详细记录额外信息 (如线程ID、Rows_examinedBytes_sent 等) OFF,强烈建议开启

如何修改配置

sql 复制代码
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5;

同时需在配置文件 my.cnf 中设置,保证重启后生效。


🔄 二、一条查询如何被判定为"慢查询"?

MySQL 处理慢查询的流程如下:
#mermaid-svg-sqcbbfyyiAoaclD3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-sqcbbfyyiAoaclD3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-sqcbbfyyiAoaclD3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-sqcbbfyyiAoaclD3 .error-icon{fill:#552222;}#mermaid-svg-sqcbbfyyiAoaclD3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-sqcbbfyyiAoaclD3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-sqcbbfyyiAoaclD3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-sqcbbfyyiAoaclD3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-sqcbbfyyiAoaclD3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-sqcbbfyyiAoaclD3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-sqcbbfyyiAoaclD3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-sqcbbfyyiAoaclD3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-sqcbbfyyiAoaclD3 .marker.cross{stroke:#333333;}#mermaid-svg-sqcbbfyyiAoaclD3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-sqcbbfyyiAoaclD3 p{margin:0;}#mermaid-svg-sqcbbfyyiAoaclD3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-sqcbbfyyiAoaclD3 .cluster-label text{fill:#333;}#mermaid-svg-sqcbbfyyiAoaclD3 .cluster-label span{color:#333;}#mermaid-svg-sqcbbfyyiAoaclD3 .cluster-label span p{background-color:transparent;}#mermaid-svg-sqcbbfyyiAoaclD3 .label text,#mermaid-svg-sqcbbfyyiAoaclD3 span{fill:#333;color:#333;}#mermaid-svg-sqcbbfyyiAoaclD3 .node rect,#mermaid-svg-sqcbbfyyiAoaclD3 .node circle,#mermaid-svg-sqcbbfyyiAoaclD3 .node ellipse,#mermaid-svg-sqcbbfyyiAoaclD3 .node polygon,#mermaid-svg-sqcbbfyyiAoaclD3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-sqcbbfyyiAoaclD3 .rough-node .label text,#mermaid-svg-sqcbbfyyiAoaclD3 .node .label text,#mermaid-svg-sqcbbfyyiAoaclD3 .image-shape .label,#mermaid-svg-sqcbbfyyiAoaclD3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-sqcbbfyyiAoaclD3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-sqcbbfyyiAoaclD3 .rough-node .label,#mermaid-svg-sqcbbfyyiAoaclD3 .node .label,#mermaid-svg-sqcbbfyyiAoaclD3 .image-shape .label,#mermaid-svg-sqcbbfyyiAoaclD3 .icon-shape .label{text-align:center;}#mermaid-svg-sqcbbfyyiAoaclD3 .node.clickable{cursor:pointer;}#mermaid-svg-sqcbbfyyiAoaclD3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-sqcbbfyyiAoaclD3 .arrowheadPath{fill:#333333;}#mermaid-svg-sqcbbfyyiAoaclD3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-sqcbbfyyiAoaclD3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-sqcbbfyyiAoaclD3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sqcbbfyyiAoaclD3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-sqcbbfyyiAoaclD3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sqcbbfyyiAoaclD3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-sqcbbfyyiAoaclD3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-sqcbbfyyiAoaclD3 .cluster text{fill:#333;}#mermaid-svg-sqcbbfyyiAoaclD3 .cluster span{color:#333;}#mermaid-svg-sqcbbfyyiAoaclD3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-sqcbbfyyiAoaclD3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-sqcbbfyyiAoaclD3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-sqcbbfyyiAoaclD3 .icon-shape,#mermaid-svg-sqcbbfyyiAoaclD3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-sqcbbfyyiAoaclD3 .icon-shape p,#mermaid-svg-sqcbbfyyiAoaclD3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-sqcbbfyyiAoaclD3 .icon-shape .label rect,#mermaid-svg-sqcbbfyyiAoaclD3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-sqcbbfyyiAoaclD3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-sqcbbfyyiAoaclD3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-sqcbbfyyiAoaclD3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否







SQL 执行完毕
日志开启?
不记录
执行时间 > long_query_time?
未使用索引

且开启了 log_queries_not_using_indexes?
标记为慢查询
检查 min_examined_row_limit
扫描行数 >= min_examined_row_limit?
写入慢查询日志

关键点

  • 执行时间实际消耗的墙钟时间,包括等待锁、I/O 等全部耗时。
  • 即使查询因为用了查询缓存(QC)而极快返回,若 QC 命中,不会计入慢查询;QC 在 8.0 已移除。
  • 若有 log_slow_extra,日志中会额外显示 Query_timeLock_time 的详细分解。

📄 三、慢查询日志的内容详解

日志文件以纯文本形式存储,每条慢查询包含若干行。一个典型条目如下(启用 log_slow_extra=ON 后信息更丰富):

text 复制代码
# Time: 2025-06-15T14:12:31.123456Z
# User@Host: app_user[app_user] @ db1 [10.0.0.5]
# Thread_id: 45  Schema: ecommerce  QC_hit: No
# Query_time: 2.234567  Lock_time: 0.000123  Rows_sent: 20  Rows_examined: 50000
# Rows_affected: 0  Bytes_sent: 2048
SET timestamp=1718460751;
SELECT id, product_name, amount FROM orders WHERE user_id=10086 ORDER BY create_time DESC LIMIT 20;
各行含义:
字段 说明
# Time 查询开始执行 的时间(UTC,若未配置 --log-timestamps 则可能为系统时区)
# User@Host 用户名、客户端主机
# Thread_id 执行该查询的线程 ID(performance_schema.threads 可关联)
Schema 使用的数据库
QC_hit 是否命中查询缓存(8.0 已无)
Query_time 总执行时间(秒),精度微秒
Lock_time 获得表锁的时间(不是行锁!InnoDB 下行锁等待不包含在这里)
Rows_sent 返回给客户端的行数
Rows_examined 扫描的行数(包括索引层扫描,是评估索引效率的关键)
Rows_affected 修改操作影响的行数
Bytes_sent 发送给客户端的字节数
SET timestamp=... 语句执行时刻的 Unix 时间戳
最后一行 实际执行的 SQL 文本

注意Lock_time 仅包含获取元数据锁存储引擎表锁 的时间。InnoDB 的行锁等待 反映在 Query_time 整体中,但未单独列出,这也是 Query_time 长而 Lock_time 短的一个常见原因。


🗃️ 四、日志存储方式:文件 vs 表

慢查询日志可同时写入文件或 mysql.slow_log 表。

存储方式 优点 缺点
文件 (File) 性能开销极小,适合长期记录;方便用外部工具分析 需登录服务器查看;需手动轮转
表 (Table) 可实时 SQL 查询分析;方便按条件过滤 产生额外写入负载,高并发下可能成为瓶颈;表位于 CSV 引擎,不支持索引(除非转为 MyISAM/InnoDB)

启用表记录

sql 复制代码
SET GLOBAL log_output = 'TABLE';  -- 也可 'FILE,TABLE'

然后查询 mysql.slow_log 即可。

⚠️ 生产环境建议使用文件方式,结合 pt-query-digest 等工具离线分析,避免干扰数据库本身。


🔧 五、慢查询日志的分析工具

1. mysqldumpslow(官方内置)

用于简单聚合慢查询日志。

bash 复制代码
# 显示出现次数最多的 5 条查询
mysqldumpslow -s c -t 5 /var/log/mysql/slow.log

# 显示平均执行时间最长的查询
mysqldumpslow -s at -t 10 slow.log
  • -s:排序依据(c 次数,t 时间,l 锁时间,r 返回行,at 平均时间等)。
  • 输出会将数字、字符串抽象为 NS,便于归类。
2. pt-query-digest(Percona Toolkit)

功能强大的命令行分析工具,能生成详细报表,包括响应时间分布、最差查询、表/索引建议等。

bash 复制代码
pt-query-digest /var/log/mysql/slow.log > digest_report.txt
  • 支持多种输入源(慢日志、通用日志、tcpdump 抓包等)。
  • 是 DBA 调优的必备神器。
3. MySQL Workbench 与第三方监控
  • MySQL Workbench 提供了图形化的慢查询分析功能。
  • 云平台(如 RDS)通常内置慢查询分析面板。
4. 利用 sys 库和 Performance Schema

MySQL 8.0 的 sys 库提供了基于 performance_schema 的汇总视图,可以实时发现近期慢查询,而不必解析日志:

sql 复制代码
SELECT * FROM sys.statement_analysis WHERE avg_latency > 0.5 ORDER BY avg_latency DESC LIMIT 10;
SELECT * FROM sys.statements_with_runtimes_in_95th_percentile;

这些视图给出了规范化的查询文本、平均延迟、行扫描等,适合快速定位问题。


💡 六、MySQL 8.0 中慢查询日志的增强

  1. log_slow_extra :前面提到的详细字段,无需额外配置即可获得 Rows_examined, Rows_sent, Thread_id 等信息,极大提升了日志的可分析性。
  2. log_slow_replica_statements:便于在从库上捕获复制的慢 SQL,排查复制延迟问题。
  3. 禁用日志时的性能 :关闭 slow_query_log 几乎无性能影响;开启后,每条 SQL 执行完会增加一次时间比对,开销极小,可以安全启用。
  4. 与 Performance Schema 的整合 :PS 的 events_statements_summary_by_digest 等表可以看作内存版的慢查询聚合,同时可通过 sys.format_statement() 函数格式化 SQL。

🎯 七、最佳实践与优化闭环

  1. 合理设置阈值:OLTP 系统设为 0.1~0.5 秒;OLAP 或报表系统可适当放宽到 1~5 秒。
  2. 定期轮转日志 :避免单个文件过大,使用 mysqladmin flush-logs 或配合 logrotate
  3. 分析而不是"看" :用 pt-query-digest 生成报表,优先优化执行次数多、总耗时高的慢查询。
  4. 与 EXPLAIN 结合 :针对每条瓶颈 SQL 执行 EXPLAIN,检查索引使用、扫描类型、是否回表等。
  5. 持续监控:将慢查询数量纳入监控告警,若突增应立即分析。
  6. 不要过度依赖 log_queries_not_using_indexes:在全表扫描但表很小的情况下,这些查询未必是问题,滥用会淹没真正重要的慢查询。

慢查询日志是性能优化的起点,而非终点。从记录到分析,再到定位具体 SQL 并优化,它构成了一个完整的性能保障闭环。掌握它,你就能在问题发生时第一时间找到证据,而不是在黑暗中猜测。