3. 慢查询基础:什么是慢查询、慢查询的典型特征、慢查询日志的开启与解读

在数据库调优领域,慢查询(Slow Query) 是性能问题的核心信号。它并非指某条 SQL 语句“写错了”,而是指其执行耗时超过了预设的阈值,成为拖垮系统吞吐量的隐形杀手。对于 GoBFF 这类高并发、低延迟的微服务架构而言,每一条慢查询都可能引发连接池饥饿、请求排队甚至雪崩效应。

3.1 什么是慢查询

慢查询是指执行时间超过数据库配置的 long_query_time 阈值的 SQL 语句。MySQL 默认阈值为 10 秒,但在生产环境中,通常建议将阈值设置为 100ms~500ms,以便尽早发现潜在的性能瓶颈。

核心观点: 慢查询 ≠ 错误查询。它可能是索引缺失、数据量膨胀、锁竞争或查询计划选择错误的结果。在 GoBFF 中,慢查询会直接导致数据库连接被长时间占用,进而引发连接池水位上升。

3.2 慢查询的典型特征

识别慢查询是调优的第一步。以下表格总结了慢查询的常见特征及其根因,帮助你在日志中快速定位问题类型:

特征类别 具体表现 常见根因
响应时间异常 单条查询耗时 > 1s,且随并发上升呈非线性增长 全表扫描、索引失效、数据倾斜
扫描行数巨大 Rows_examined 远大于 Rows_sent(如 10 万行扫描仅返回 10 行) 缺少合适的索引、查询条件选择性差
锁等待频繁 查询长时间处于 Waiting for table metadata lock 或行锁等待状态 长事务未提交、DDL 操作阻塞、并发写冲突
临时表使用 查询计划中出现 Using temporary 或 Using filesort GROUP BY / ORDER BY 无索引、JOIN 顺序不当
连接池水位联动 慢查询期间,GoBFF 连接池活跃连接数持续接近上限 单条慢查询阻塞连接释放,导致后续请求排队
实战经验: 在 GoBFF 的慢查询日志中,Rows_examined 与 Rows_sent 的比值超过 100:1 时,几乎可以断定索引缺失。此时应优先检查 WHERE 条件列和 JOIN 关联列是否已建立索引。

3.3 慢查询日志的开启与解读

MySQL 的慢查询日志是诊断性能问题的第一手资料。以下步骤演示如何正确开启并解读日志内容。

3.3.1 开启慢查询日志

在 MySQL 配置文件(my.cnf 或 my.ini)中添加以下配置,并重启 MySQL 服务:

[mysqld]
# 开启慢查询日志
slow_query_log = ON

# 日志文件路径(确保 MySQL 用户有写入权限)
slow_query_log_file = /var/log/mysql/slow-query.log

# 慢查询阈值(单位:秒,推荐设置为 0.1~0.5)
long_query_time = 0.5

# 记录未使用索引的查询(可选,用于发现潜在问题)
log_queries_not_using_indexes = ON

# 每分钟记录一次未使用索引的查询摘要(避免日志过大)
log_throttle_queries_not_using_indexes = 60

你也可以在运行时动态开启(无需重启):

-- 设置全局变量
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.5;
SET GLOBAL log_queries_not_using_indexes = ON;

-- 确认配置生效
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';

3.3.2 慢查询日志解读

以下是一条典型的慢查询日志记录,我们逐字段拆解:

# Time: 2025-03-15T10:23:45.123456Z
# User@Host: gobff_app[gobff_app] @  [10.0.1.100]  Id: 12345
# Query_time: 2.345678  Lock_time: 0.001234  Rows_sent: 10  Rows_examined: 50000
SET timestamp=1742029425;
SELECT u.id, u.name, o.order_id, o.amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = 'active'
ORDER BY u.created_at DESC
LIMIT 10;

关键字段解读:

  • Query_time:查询总耗时(2.35 秒),远超 0.5 秒阈值,是慢查询的直接证据。
  • Lock_time:锁等待时间(0.001 秒),此处锁竞争不严重。
  • Rows_sent:最终返回给客户端的行数(10 行)。
  • Rows_examined:实际扫描的行数(50000 行)。扫描行数与返回行数比值为 5000:1,说明查询效率极低。
  • SET timestamp:查询执行的时间戳,用于关联业务日志。
注意: 在 GoBFF 生产环境中,如果 Rows_examined 超过 10000 行且 Query_time 超过 1 秒,应立即触发告警。这类查询不仅自身慢,还会占用连接池连接,导致其他正常请求等待。

3.3.3 慢查询日志分析工具

手动逐条阅读日志效率低下,推荐使用以下工具进行聚合分析:

工具名称 适用场景 核心功能
pt-query-digest Percona Toolkit 核心组件,适合深度分析 按查询指纹聚合、排序、输出执行计划建议
mysqldumpslow MySQL 自带工具,轻量快速 按平均耗时、扫描行数排序,支持分组统计
Performance Schema MySQL 5.7+ 内置,无需额外安装 实时监控查询性能,支持按 digest 汇总

使用 pt-query-digest 分析慢查询日志的示例命令:

pt-query-digest /var/log/mysql/slow-query.log > /tmp/slow_analysis.txt

输出结果会按 总耗时 降序排列,并给出每个查询指纹的 Query_time 分布(P95、P99 等),帮助你快速定位最需要优化的查询。

3.4 慢查询与连接池水位的关联

下图展示了慢查询如何影响 GoBFF 连接池的水位变化:

慢查询 → 连接池水位上升链路 正常请求 慢查询 连接池 活跃连接数 ↑ 请求排队 超时 / 熔断 链路说明: 1. 正常请求快速执行,连接及时释放,连接池水位平稳。 2. 慢查询长时间占用连接,连接池活跃连接数持续上升。 3. 当活跃连接数达到连接池上限,新请求进入排队或直接超时熔断。
关键结论: 慢查询是连接池水位上升的 直接推手。在 GoBFF 中,每消除一条慢查询,相当于释放了连接池中一个长期占用的连接槽位,从而降低整体水位,提升系统吞吐量。

3.5 本章小结

  • 慢查询定义:执行时间超过 long_query_time 阈值的 SQL,是连接池水位的“放大器”。
  • 典型特征:高扫描行数、锁等待、临时表使用、响应时间异常。
  • 日志开启:通过配置文件或动态变量开启,推荐阈值 0.5 秒。
  • 日志解读:重点关注 Query_time、Rows_examined、Rows_sent 三个字段。
  • 工具辅助:使用 pt-query-digest 或 mysqldumpslow 进行聚合分析。