外观
MariaDB 慢查询日志配置与分析
慢查询日志概述
什么是慢查询日志
慢查询日志是 MariaDB 记录执行时间超过阈值的 SQL 查询的日志文件,是 DBA 进行性能优化的重要工具。通过分析慢查询日志,可以识别出效率低下的 SQL 语句,找出性能瓶颈,从而进行针对性优化。
慢查询日志的作用
- 识别执行效率低下的 SQL 语句
- 分析查询执行计划,找出索引问题
- 发现数据库设计不合理的地方
- 监控数据库性能趋势
- 为容量规划提供依据
慢查询日志配置
配置参数
| 参数名称 | 默认值 | 描述 |
|---|---|---|
| slow_query_log | OFF | 是否启用慢查询日志 |
| slow_query_log_file | (平台相关) | 慢查询日志文件路径 |
| long_query_time | 10.0 | 慢查询阈值,单位秒 |
| log_queries_not_using_indexes | OFF | 是否记录未使用索引的查询 |
| log_slow_admin_statements | OFF | 是否记录慢管理语句(如 ALTER TABLE) |
| log_slow_filter | admin,filesort,filesort_on_disk,full_join,full_scan,query_cache,query_cache_miss,tmp_table,tmp_table_on_disk | 慢查询过滤条件 |
| log_slow_rate_limit | 1 | 慢查询日志记录频率限制 |
| log_slow_verbosity | query_plan,explain | 慢查询日志详细程度 |
| min_examined_row_limit | 0 | 记录查询的最小扫描行数 |
配置示例
ini
# my.cnf
[mysqld]
# 启用慢查询日志
slow_query_log = ON
# 慢查询日志文件路径
slow_query_log_file = /var/log/mariadb/mariadb-slow.log
# 慢查询阈值,设置为 1 秒
long_query_time = 1
# 记录未使用索引的查询
log_queries_not_using_indexes = ON
# 记录慢管理语句
log_slow_admin_statements = ON
# 慢查询过滤条件
log_slow_filter = admin,filesort,filesort_on_disk,full_join,full_scan,query_cache,query_cache_miss,tmp_table,tmp_table_on_disk
# 慢查询日志详细程度
log_slow_verbosity = query_plan,explain
# 记录查询的最小扫描行数
min_examined_row_limit = 100动态调整配置
sql
-- 启用慢查询日志
SET GLOBAL slow_query_log = ON;
-- 设置慢查询阈值为 2 秒
SET GLOBAL long_query_time = 2;
-- 启用记录未使用索引的查询
SET GLOBAL log_queries_not_using_indexes = ON;
-- 查看当前慢查询日志配置
SHOW VARIABLES LIKE 'slow_query%';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'log_queries_not_using_indexes';慢查询日志管理
日志文件位置
| 操作系统 | 默认慢查询日志路径 |
|---|---|
| Linux (RPM) | /var/log/mariadb/mariadb-slow.log |
| Linux (Debian) | /var/log/mysql/mysql-slow.log |
| Windows | 数据目录下的 hostname-slow.log |
| macOS | /usr/local/var/log/mariadb-slow.log |
日志轮换
使用 logrotate 进行日志轮换
ini
# /etc/logrotate.d/mariadb
/var/log/mariadb/mariadb-slow.log {
daily
rotate 7
missingok
compress
delaycompress
notifempty
create 640 mysql mysql
sharedscripts
postrotate
# 刷新慢查询日志
/usr/bin/mysqladmin flush-slow-logs
endscript
}手动进行日志轮换
bash
# 重命名当前慢查询日志
mv /var/log/mariadb/mariadb-slow.log /var/log/mariadb/mariadb-slow.log.old
# 刷新慢查询日志,生成新的日志文件
mysqladmin flush-slow-logs日志清理策略
| 日志类型 | 保留时间 | 清理方式 |
|---|---|---|
| 生产环境慢查询日志 | 7-30天 | logrotate 自动清理 |
| 测试环境慢查询日志 | 3-7天 | logrotate 自动清理 |
| 归档日志 | 1年 | 压缩存储到归档目录 |
慢查询日志格式
日志条目格式
# Time: 2025-12-27T10:15:30.123456Z
# User@Host: user[user] @ localhost [] Id: 12345
# Query_time: 2.500000 Lock_time: 0.100000 Rows_sent: 100 Rows_examined: 10000
# Thread_id: 12345 Schema: mydb QC_hit: No
# Rows_affected: 0 Bytes_sent: 10240
# Full_scan: Yes Full_join: No Tmp_table: Yes Tmp_table_on_disk: No
# Filesort: Yes Filesort_on_disk: No Merge_passes: 0
# InnoDB_IO_r_ops: 0 InnoDB_IO_r_bytes: 0 InnoDB_IO_w_ops: 0 InnoDB_IO_w_bytes: 0
# InnoDB_pages_distinct: 1000
use mydb;
SET timestamp=1735325730;
SELECT * FROM mytable WHERE created_at < '2025-01-01' ORDER BY id DESC LIMIT 100;日志字段说明
| 字段名称 | 描述 |
|---|---|
| Time | 查询执行时间 |
| User@Host | 执行查询的用户和主机 |
| Id | 连接 ID |
| Query_time | 查询执行时间,单位秒 |
| Lock_time | 锁定时间,单位秒 |
| Rows_sent | 返回的行数 |
| Rows_examined | 扫描的行数 |
| Thread_id | 线程 ID |
| Schema | 数据库名称 |
| QC_hit | 是否命中查询缓存 |
| Rows_affected | 影响的行数 |
| Bytes_sent | 发送的数据量 |
| Full_scan | 是否全表扫描 |
| Full_join | 是否全连接 |
| Tmp_table | 是否使用临时表 |
| Tmp_table_on_disk | 是否使用磁盘临时表 |
| Filesort | 是否使用文件排序 |
| Filesort_on_disk | 是否使用磁盘文件排序 |
| Merge_passes | 合并排序的次数 |
| InnoDB_IO_r_ops | InnoDB 读操作次数 |
| InnoDB_IO_r_bytes | InnoDB 读字节数 |
| InnoDB_IO_w_ops | InnoDB 写操作次数 |
| InnoDB_IO_w_bytes | InnoDB 写字节数 |
| InnoDB_pages_distinct | InnoDB 访问的不同页面数 |
慢查询日志分析
常用分析命令
查看慢查询数量
bash
# 统计慢查询数量
wc -l /var/log/mariadb/mariadb-slow.log
# 查看前 10 条慢查询
tail -n 100 /var/log/mariadb/mariadb-slow.log | grep -A 10 "# Query_time"查找特定类型的慢查询
bash
# 查找全表扫描的慢查询
grep -B 5 -A 10 "Full_scan: Yes" /var/log/mariadb/mariadb-slow.log
# 查找使用磁盘临时表的慢查询
grep -B 5 -A 10 "Tmp_table_on_disk: Yes" /var/log/mariadb/mariadb-slow.log
# 查找使用磁盘文件排序的慢查询
grep -B 5 -A 10 "Filesort_on_disk: Yes" /var/log/mariadb/mariadb-slow.log分析慢查询执行时间
bash
# 提取慢查询执行时间并排序
grep "Query_time:" /var/log/mariadb/mariadb-slow.log | awk '{print $2}' | sort -nr | head -10慢查询日志分析工具
mysqldumpslow
MariaDB 自带的慢查询日志分析工具:
bash
# 安装位置
which mysqldumpslow
# 分析慢查询日志,按执行次数排序
mysqldumpslow -s c -t 10 /var/log/mariadb/mariadb-slow.log
# 按执行时间排序
mysqldumpslow -s t -t 10 /var/log/mariadb/mariadb-slow.log
# 按锁定时间排序
mysqldumpslow -s l -t 10 /var/log/mariadb/mariadb-slow.log
# 按返回行数排序
mysqldumpslow -s r -t 10 /var/log/mariadb/mariadb-slow.log
# 过滤特定数据库的慢查询
mysqldumpslow -d mydb -t 10 /var/log/mariadb/mariadb-slow.logpt-query-digest
Percona Toolkit 中的慢查询日志分析工具,功能更强大:
bash
# 安装 Percona Toolkit
sudo apt-get install percona-toolkit
# 基本使用
pt-query-digest /var/log/mariadb/mariadb-slow.log
# 保存分析结果到文件
pt-query-digest /var/log/mariadb/mariadb-slow.log > slow_query_analysis.txt
# 分析特定时间段的慢查询
pt-query-digest --since '2025-12-27 10:00:00' --until '2025-12-27 11:00:00' /var/log/mariadb/mariadb-slow.log
# 按查询类型分析
pt-query-digest --group-by query /var/log/mariadb/mariadb-slow.log
# 按用户分析
pt-query-digest --group-by user /var/log/mariadb/mariadb-slow.log
# 按主机分析
pt-query-digest --group-by host /var/log/mariadb/mariadb-slow.logMariaDB Enterprise Monitor
商业版 MariaDB 提供的监控工具,包含慢查询分析功能:
- 实时监控慢查询
- 可视化展示慢查询趋势
- 自动识别性能问题
- 提供优化建议
常见慢查询问题分析
全表扫描
现象
日志中显示 Full_scan: Yes,扫描行数远大于返回行数。
原因
- 没有合适的索引
- 查询条件使用了函数或表达式,导致索引失效
- 查询条件使用了
!=、<>、NOT IN等操作符 - 查询条件使用了
OR连接,且没有针对所有条件的联合索引
优化建议
- 为查询条件添加合适的索引
- 避免在查询条件中使用函数或表达式
- 尽量使用
IN代替NOT IN - 考虑使用
UNION代替OR
磁盘临时表
现象
日志中显示 Tmp_table_on_disk: Yes。
原因
- 查询结果集过大,超过
tmp_table_size或max_heap_table_size - 查询使用了 BLOB 或 TEXT 类型字段
- 查询包含
GROUP BY和ORDER BY操作,且字段不同
优化建议
- 增加
tmp_table_size和max_heap_table_size参数值 - 避免在临时表中使用 BLOB 或 TEXT 类型
- 优化
GROUP BY和ORDER BY操作,尽量使用相同的字段 - 考虑使用索引覆盖查询
磁盘文件排序
现象
日志中显示 Filesort_on_disk: Yes。
原因
- 查询结果集过大,超过
sort_buffer_size - 查询包含复杂的
ORDER BY操作 - 查询使用了
DISTINCT和ORDER BY操作
优化建议
- 增加
sort_buffer_size参数值 - 优化
ORDER BY操作,尽量使用索引排序 - 避免在查询中同时使用
DISTINCT和复杂的ORDER BY - 考虑使用索引覆盖查询
长锁定时间
现象
日志中显示 Lock_time 较大。
原因
- 表级锁争用(如 MyISAM 存储引擎)
- 行级锁争用(如 InnoDB 存储引擎)
- 事务持有锁时间过长
- 死锁导致的等待
优化建议
- 对于高并发场景,使用 InnoDB 存储引擎
- 优化事务设计,尽量缩短事务持有锁的时间
- 避免在事务中执行长时间的操作
- 优化索引,减少锁冲突
慢查询优化步骤
1. 收集慢查询日志
启用慢查询日志,设置合适的阈值,收集足够的慢查询数据。
2. 分析慢查询日志
使用 pt-query-digest 等工具分析慢查询日志,识别出最需要优化的查询。
3. 查看执行计划
对慢查询语句使用 EXPLAIN 命令查看执行计划,找出问题所在。
sql
EXPLAIN SELECT * FROM mytable WHERE created_at < '2025-01-01' ORDER BY id DESC LIMIT 100;4. 优化查询语句
根据执行计划,优化查询语句:
- 简化查询,减少不必要的字段和表连接
- 优化查询条件,避免全表扫描
- 合理使用索引
- 避免在查询中使用函数或表达式
5. 优化数据库结构
- 优化表结构,合理设计字段类型
- 优化索引,添加必要的索引,删除冗余索引
- 考虑分表、分区等方式,分散数据量
6. 优化配置参数
根据慢查询分析结果,调整相关配置参数:
tmp_table_size和max_heap_table_sizesort_buffer_sizeread_buffer_size和read_rnd_buffer_sizejoin_buffer_size
7. 验证优化效果
优化后,再次运行查询,比较执行时间,验证优化效果。
慢查询日志最佳实践
配置最佳实践
合理设置慢查询阈值
- 生产环境:根据业务需求设置,一般为 1-5 秒
- 测试环境:可以设置更小的值,如 0.1 秒,便于发现潜在问题
- 避免设置过小,导致日志过大;避免设置过大,导致漏报慢查询
启用合适的过滤条件
- 根据需要调整
log_slow_filter参数 - 记录关键的慢查询信息,如全表扫描、磁盘临时表等
- 根据需要调整
设置合适的日志详细程度
- 启用
query_plan和explain,便于分析执行计划 - 根据需要调整
log_slow_verbosity参数
- 启用
定期轮换日志
- 使用
logrotate定期轮换日志 - 压缩归档旧日志,便于后续查询
- 使用
分析最佳实践
定期分析慢查询日志
- 每天或每周分析一次慢查询日志
- 及时发现和解决性能问题
建立慢查询基线
- 记录正常情况下的慢查询数量和类型
- 当慢查询数量或类型发生明显变化时,及时预警
结合其他监控数据
- 将慢查询分析与系统监控、数据库监控结合
- 全面了解数据库性能状况
持续优化
- 建立慢查询优化的持续改进机制
- 定期回顾和优化已优化的查询
常见问题(FAQ)
Q: 如何确定慢查询阈值?
A: 确定慢查询阈值的方法:
- 参考业务需求,核心业务查询一般要求在 1 秒内完成
- 分析历史查询性能数据,找出 95% 或 99% 的查询执行时间
- 根据系统负载调整,高负载时可以适当提高阈值
- 考虑使用动态阈值,根据不同时间段调整
Q: 慢查询日志过大怎么办?
A: 解决方法:
- 调整慢查询阈值,减少日志量
- 优化慢查询,减少慢查询数量
- 增加日志轮换频率
- 考虑使用
log_slow_rate_limit限制日志记录频率 - 使用集中式日志管理平台,如 ELK Stack
Q: 为什么有些慢查询没有被记录?
A: 可能的原因:
- 慢查询阈值设置过高,导致这些查询没有达到记录条件
- 慢查询日志没有启用
log_slow_filter设置不当,过滤掉了这些查询log_slow_rate_limit设置过小,限制了日志记录- 查询执行时间受系统负载影响,有时快有时慢
Q: 如何实时监控慢查询?
A: 实时监控方法:
- 使用
tail -f命令实时查看慢查询日志 - 集成到集中式日志管理平台,设置实时告警
- 使用 MariaDB Enterprise Monitor 等商业监控工具
- 编写自定义脚本,定期分析慢查询日志并发送告警
Q: 慢查询优化后性能没有提升怎么办?
A: 排查步骤:
- 重新分析执行计划,确认优化是否生效
- 检查是否有其他因素影响性能,如系统负载、磁盘 I/O 等
- 考虑调整其他相关配置参数
- 检查是否存在锁争用或死锁问题
- 考虑优化数据库结构或应用程序设计
总结
慢查询日志是 MariaDB 性能优化的重要工具,通过合理配置和有效分析慢查询日志,可以识别出效率低下的 SQL 语句,找出性能瓶颈,从而进行针对性优化。建议 DBA 建立完善的慢查询日志管理机制,包括合理的配置、定期的分析、持续的优化和自动化的监控,以确保数据库系统的高性能运行。
