2. MySQL 慢查询日志:开启与配置 slow_query_log、long_query_time 参数详解
慢查询日志是 MySQL 性能诊断的“第一现场”。在 Go 项目中,数据库交互频繁,一条慢 SQL 可能拖垮整个接口的响应时间。本章将深入解析慢查询日志的核心参数 slow_query_log 与 long_query_time,并给出生产环境的推荐配置。
2.1 慢查询日志的作用与原理
慢查询日志记录了执行时间超过指定阈值的 SQL 语句。MySQL 在语句执行结束后,判断其实际执行时间是否大于 long_query_time(单位:秒),若超过则将该语句及其执行信息写入日志文件或系统表。
关键点:慢查询日志只记录执行完成的语句,不记录被锁等待或未完成的语句。因此,日志中的时间是“实际执行时间”,不包含锁等待时间。
2.2 核心参数详解
| 参数名 | 作用域 | 默认值 | 推荐值(生产) | 说明 |
|---|---|---|---|---|
slow_query_log |
全局/会话 | OFF | ON | 是否开启慢查询日志 |
slow_query_log_file |
全局 | 主机名-slow.log | /var/log/mysql/slow.log | 慢查询日志文件路径 |
long_query_time |
全局/会话 | 10.000000 | 0.5 ~ 2.0 | 超过此时间的 SQL 被记录(单位:秒) |
log_queries_not_using_indexes |
全局 | OFF | ON | 记录未使用索引的查询(即使执行很快) |
log_slow_admin_statements |
全局 | OFF | ON | 记录慢的管理语句(ALTER、ANALYZE 等) |
min_examined_row_limit |
全局/会话 | 0 | 100 | 扫描行数超过此值才记录(避免记录小查询) |
2.3 开启与配置方法
2.3.1 动态开启(无需重启)
-- 开启慢查询日志
SET GLOBAL slow_query_log = ON;
-- 设置日志文件路径
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';
-- 设置阈值:超过 1 秒的 SQL 被记录
SET GLOBAL long_query_time = 1;
-- 记录未使用索引的查询
SET GLOBAL log_queries_not_using_indexes = ON;
-- 记录慢管理语句
SET GLOBAL log_slow_admin_statements = ON;
提示:动态设置
long_query_time 后,已存在的连接不会立即生效,需要重新连接或执行 SET SESSION long_query_time = 1; 才会应用新值。
2.3.2 持久化配置(写入 my.cnf)
[mysqld]
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 1
log_queries_not_using_indexes = ON
log_slow_admin_statements = ON
min_examined_row_limit = 100
2.4 生产环境推荐配置
针对 Go 项目的高并发场景,推荐以下配置组合:
- long_query_time = 0.5:对于核心接口,超过 500ms 的 SQL 就应被记录并优化。
- log_queries_not_using_indexes = ON:强制记录全表扫描的查询,避免索引缺失导致性能问题。
- min_examined_row_limit = 100:过滤掉扫描行数极少的查询,减少日志噪音。
- log_slow_admin_statements = ON:DDL 操作(如 ALTER TABLE)在数据量大时可能很慢,需要监控。
注意:慢查询日志会消耗磁盘 I/O。在 QPS 极高的场景下,建议将日志输出到独立的磁盘分区,或使用
mysql.slow_log 系统表配合定期清理策略,避免日志文件无限增长。
2.5 日志内容解读
一条典型的慢查询日志记录包含以下字段:
# Time: 2025-03-21T10:15:30.123456Z
# User@Host: go_app[go_app] @ [10.0.0.1] Id: 12345
# Query_time: 2.345678 Lock_time: 0.000123 Rows_sent: 100 Rows_examined: 50000
SET timestamp=1742544930;
SELECT * FROM orders WHERE status = 'pending' ORDER BY created_at DESC;
| 字段 | 含义 | 关注点 |
|---|---|---|
Query_time |
SQL 实际执行时间(秒) | 超过 long_query_time 即被记录 |
Lock_time |
锁等待时间(秒) | 值过大说明存在锁竞争 |
Rows_sent |
返回给客户端的行数 | 过大可能意味着未加 LIMIT |
Rows_examined |
扫描的行数 | 远大于 Rows_sent 说明索引效率低 |
2.6 常见问题与避坑
- 日志文件过大:建议启用日志轮转(如
logrotate)或定期清理。 - 阈值设置过低:
long_query_time = 0会记录所有 SQL,导致日志爆炸,仅用于短期调试。 - 忽略管理语句:
ALTER TABLE在数据量大时可能执行数分钟,务必开启log_slow_admin_statements。 - 会话级覆盖:某些 ORM 框架(如 GORM)可能设置会话级
long_query_time,导致全局配置不生效,需检查应用代码。
Go 项目实践:在 Go 中,可以通过
database/sql 的 SetConnMaxLifetime 和 SetMaxIdleConns 控制连接池行为,但慢查询日志的配置仍需在 MySQL 端完成。建议在 CI/CD 流程中加入慢查询日志分析步骤,自动发现新增的慢 SQL。