2. MySQL 慢查询日志:开启与配置 slow_query_log、long_query_time 参数详解

慢查询日志是 MySQL 性能诊断的“第一现场”。在 Go 项目中,数据库交互频繁,一条慢 SQL 可能拖垮整个接口的响应时间。本章将深入解析慢查询日志的核心参数 slow_query_log 与 long_query_time,并给出生产环境的推荐配置。

2.1 慢查询日志的作用与原理

慢查询日志记录了执行时间超过指定阈值的 SQL 语句。MySQL 在语句执行结束后,判断其实际执行时间是否大于 long_query_time(单位:秒),若超过则将该语句及其执行信息写入日志文件或系统表。

关键点:慢查询日志只记录执行完成的语句,不记录被锁等待或未完成的语句。因此,日志中的时间是“实际执行时间”,不包含锁等待时间。
慢查询日志记录流程 客户端发送 SQL MySQL 执行 SQL 判断执行时间 写入慢查询日志 返回结果给客户端 超过阈值 未超过阈值

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。