慢查询日志是 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_examined、Bytes_sent 等) |
OFF,强烈建议开启 |
如何修改配置:
sqlSET 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_time与Lock_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平均时间等)。- 输出会将数字、字符串抽象为
N、S,便于归类。
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 中慢查询日志的增强
log_slow_extra:前面提到的详细字段,无需额外配置即可获得Rows_examined,Rows_sent,Thread_id等信息,极大提升了日志的可分析性。log_slow_replica_statements:便于在从库上捕获复制的慢 SQL,排查复制延迟问题。- 禁用日志时的性能 :关闭
slow_query_log几乎无性能影响;开启后,每条 SQL 执行完会增加一次时间比对,开销极小,可以安全启用。 - 与 Performance Schema 的整合 :PS 的
events_statements_summary_by_digest等表可以看作内存版的慢查询聚合,同时可通过sys.format_statement()函数格式化 SQL。
🎯 七、最佳实践与优化闭环
- 合理设置阈值:OLTP 系统设为 0.1~0.5 秒;OLAP 或报表系统可适当放宽到 1~5 秒。
- 定期轮转日志 :避免单个文件过大,使用
mysqladmin flush-logs或配合logrotate。 - 分析而不是"看" :用
pt-query-digest生成报表,优先优化执行次数多、总耗时高的慢查询。 - 与 EXPLAIN 结合 :针对每条瓶颈 SQL 执行
EXPLAIN,检查索引使用、扫描类型、是否回表等。 - 持续监控:将慢查询数量纳入监控告警,若突增应立即分析。
- 不要过度依赖
log_queries_not_using_indexes:在全表扫描但表很小的情况下,这些查询未必是问题,滥用会淹没真正重要的慢查询。
慢查询日志是性能优化的起点,而非终点。从记录到分析,再到定位具体 SQL 并优化,它构成了一个完整的性能保障闭环。掌握它,你就能在问题发生时第一时间找到证据,而不是在黑暗中猜测。