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 连接池的水位变化:
关键结论: 慢查询是连接池水位上升的 直接推手。在 GoBFF 中,每消除一条慢查询,相当于释放了连接池中一个长期占用的连接槽位,从而降低整体水位,提升系统吞吐量。
3.5 本章小结
- 慢查询定义:执行时间超过
long_query_time阈值的 SQL,是连接池水位的“放大器”。 - 典型特征:高扫描行数、锁等待、临时表使用、响应时间异常。
- 日志开启:通过配置文件或动态变量开启,推荐阈值 0.5 秒。
- 日志解读:重点关注
Query_time、Rows_examined、Rows_sent三个字段。 - 工具辅助:使用
pt-query-digest或mysqldumpslow进行聚合分析。