目录
-
- 一、什么是慢SQL日志?
- 二、慢查询日志的开启与配置
-
-
- 配置文件方式(永久生效)
-
- 动态开启(不需重启,适合临时调试)
-
- MySQL 8.0+ 新特性
-
- 三、慢查询日志的内容格式
- 四、慢查询日志分析工具
-
-
- mysqldumpslow(MySQL内置)
-
- pt-query-digest
-
- 其他工具
-
- 五、一些使用时需注意的
-
- 1.日志无限扩增问题
-
- **性能开销**
在数据库性能优化中,慢SQL日志(Slow Query Log)是最基础、最重要的诊断工具之一。它能自动记录所有执行时间超过指定阈值的SQL语句,帮助开发者与DBA快速发现潜在的性能问题。本文以MySQL为例,详细讲解慢SQL日志的原理、配置、使用、分析以及最佳实践。
一、什么是慢SQL日志?
慢SQL日志是数据库内置的一种日志机制,用于记录执行时间较长的查询语句。
MySQL中默认阈值是10秒(long_query_time = 10),但在生产环境中通常会调低到1秒甚至0.5秒。
除了执行时间超长的查询,还可以选择记录:
- 未使用索引的查询(即使执行很快)。
- 不进入查询缓存的查询(旧版本MySQL)。
慢查询日志的用途:
- 发现最耗时的SQL,优先优化。
- 分析系统整体查询性能分布。
- 作为性能基线,监控性能变化。
二、慢查询日志的开启与配置
1. 配置文件方式(永久生效)
编辑MySQL配置文件(通常是/etc/my.cnf或/etc/mysql/my.cnf)的[mysqld]部分:
ini
[mysqld]
slow_query_log = ON # 开启慢查询日志
slow_query_log_file = /var/log/mysql/slow.log # 日志文件路径
long_query_time = 1 # 阈值1秒(支持小数,如0.5)
log_queries_not_using_indexes = ON # 可选:记录未使用索引的查询
min_examined_row_limit = 100 # 可选:扫描行数超过该值才记录(避免小表全扫)
log_slow_admin_statements = ON # 可选:记录慢的管理员语句(如ALTER TABLE)
log_slow_slave_statements = ON # 可选:从库记录慢查询(复制环境)
修改后重启MySQL服务生效。
2. 动态开启(不需重启,适合临时调试)
sql
-- 开启慢查询日志
SET GLOBAL slow_query_log = ON;
-- 设置日志文件路径(需有权限)
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
-- 设置阈值(支持毫秒级,如0.1表示100ms)
SET GLOBAL long_query_time = 1;
-- 记录未使用索引的查询
SET GLOBAL log_queries_not_using_indexes = ON;
注意:动态设置在MySQL重启后会失效。
3. MySQL 8.0+ 新特性
- 支持微秒级精度阈值(long_query_time可设为0.000001)。
- 默认日志格式为文本文件,也支持写入performance_schema或系统表。
三、慢查询日志的内容格式
一条典型的慢查询日志记录包含以下部分:
ini
# Time: 2025-12-18T10:23:45.123456Z
# User@Host: app_user[app_user] @ 192.168.1.100 []
# Thread_id: 12345 Schema: mydb QC_hit: 0
# Query_time: 2.345678 Lock_time: 0.000123 Rows_sent: 1000 Rows_examined: 1000000
# Rows_affected: 0
SET timestamp=1734517425;
SELECT * FROM orders WHERE create_time > '2025-01-01' ORDER BY id DESC;
关键字段解释:
- Query_time:查询执行总耗时(最重要)。
- Lock_time:获取锁等待时间。
- Rows_sent:返回给客户端的行数。
- Rows_examined:扫描的行数(越大越说明可能缺少索引)。
- SET timestamp:查询开始执行的时间戳。
- 最后是完整的SQL语句。
四、慢查询日志分析工具
手动查看日志效率低下,推荐使用专业工具:
1. mysqldumpslow(MySQL内置)
bash
# 按执行时间排序,取前10条
mysqldumpslow -t 10 /var/log/mysql/slow.log
# 按平均执行时间排序
mysqldumpslow -s al -t 10 /var/log/mysql/slow.log
常用参数:
- -s:排序方式(c:次数, t:时间, al:平均时间等)
- -t:返回前N条
- -g:配合正则过滤特定SQL
2. pt-query-digest
功能强大,支持详细统计报告。
bash
pt-query-digest /var/log/mysql/slow.log > slow_report.txt
输出包括:
- 排名前N的慢查询(按总时间、平均时间、执行次数)。
- 每条SQL的摘要(指纹,去除常量)。
- 执行计划统计(Rows examine、Lock time等)。
- 查询时间分布图。
示例报告片段:
ini
# Rank Query ID Response time Calls R/Call Item
# ==== ================== ============== ====== ======= ============
# 1 0xB7E4... 1234.5678 80% 500 2.469 SELECT orders
3. 其他工具
- EverSQL Query Optimizer:在线分析。
- MySQL Workbench / Navicat:图形化查看。
- ELK栈或阿里云/腾讯云的日志服务:实时监控。
五、一些使用时需注意的
1.日志无限扩增问题
慢查询日志默认写入文件(如/var/log/mysql/slow.log),高并发或阈值设置过低时,日志会快速膨胀。核心解决思路是定期轮转(rotation)和归档,结合压缩和自动清理。
解决方案.:使用logrotate实现自动轮转
Linux系统自带的logrotate工具是最成熟的方案,能每天/每周自动切割日志、压缩旧文件、保留有限份数。
步骤:
创建或编辑/etc/logrotate.d/mysql-slow文件(路径可能因系统略有差异):
/var/log/mysql/mysql-slow.log { # 替换为你的实际日志路径
daily # 每天轮转一次(可改weekly)
rotate 14 # 保留14份旧日志(约2周)
compress # 压缩旧日志(gzip格式,节省空间)
delaycompress # 延迟压缩,上一份日志先不压
missingok # 如果日志不存在,不报错
notifempty # 空日志不轮转
create 660 mysql mysql # 新日志文件权限和属主
sharedscripts
postrotate
# 安全刷新日志:临时关闭慢查询,等待缓冲flush,再恢复
/usr/bin/mysql -e "
SET @old = @@global.long_query_time;
SET GLOBAL slow_query_log = OFF;
SELECT SLEEP(5); # 等待缓冲写入
FLUSH SLOW LOGS;
SET GLOBAL slow_query_time = @old;
SET GLOBAL slow_query_log = ON;
" > /dev/null 2>&1
endscript
}
说明:
postrotate脚本中临时关闭慢查询日志,然后FLUSH SLOW LOGS让MySQL重新打开新文件,避免MySQL继续写旧文件(因为MySQL持有文件句柄),继续写旧文件可能会找不到文件,无法写入。- 更安全的变体:使用
copytruncate选项(不需flush,但可能丢失少量日志)。 - 测试配置:
sudo logrotate -d /etc/logrotate.d/mysql-slow(调试模式)。 - 强制执行:
sudo logrotate -f /etc/logrotate.d/mysql-slow。
这样配置后,日志每天切割,旧日志压缩保留14天,磁盘占用可控。
2. 性能开销
慢查询日志本身开销很小(仅记录超阈值查询),但如果阈值过低或开启log_queries_not_using_indexes,会记录大量语句,增加I/O负担,所以设置合理的阈值是很有必要的。
注:如果表内数据比较少也用不到慢查询日志,本文仅针对大表导致的慢sql问题。