3. 慢查询基础概念

3.1 什么是慢查询

慢查询(Slow Query)是指执行时间超过预设阈值的数据库查询操作。在报表导出场景中,慢查询通常表现为:用户点击导出后长时间等待、页面超时、导出文件生成延迟等。慢查询是数据库性能问题的直接体现,也是系统性能优化的首要排查目标。

核心定义: 慢查询 = 执行时间 > 阈值(通常为 100ms ~ 1s)。在 MySQL 中,通过 long_query_time 参数定义;在 Redis 中,通过 slowlog-log-slower-than 参数定义。

3.2 慢查询的常见原因

慢查询的产生通常由以下四类因素导致,它们可能单独出现,也可能叠加作用:

原因分类 典型表现 报表导出场景示例
索引缺失 全表扫描、扫描行数远大于返回行数 按日期范围查询百万级订单表时未建索引
数据量大 单表行数过亿、单次查询返回大量行 导出全年销售明细,一次读取 500 万行
锁竞争 行锁等待、表锁阻塞、死锁回滚 导出期间其他事务批量更新同一张表
网络延迟 TCP 往返次数多、带宽瓶颈 应用服务器与数据库跨机房部署
注意: 在报表导出服务中,索引缺失 和 数据量大 是最常见的两类原因,占比超过 70%。锁竞争和网络延迟往往在并发导出或跨区域部署时才会凸显。

3.3 慢查询日志的配置与解读

慢查询日志是排查慢查询的第一手资料。以下分别介绍 MySQL 和 Redis 的慢查询日志配置方法。

3.3.1 MySQL 慢查询日志配置

MySQL 通过一组系统变量控制慢查询日志的行为:

参数名 默认值 推荐值(报表导出场景) 说明
slow_query_log OFF ON 开启慢查询日志
long_query_time 10.000000 0.5 ~ 1.0 超过此时间的查询被记录(单位:秒)
log_queries_not_using_indexes OFF ON 记录未使用索引的查询(即使很快)
slow_query_log_file 主机名-slow.log /var/log/mysql/slow-query.log 日志文件路径
min_examined_row_limit 0 1000 仅记录扫描行数超过此值的查询

配置示例(动态生效,无需重启):

-- 开启慢查询日志
SET GLOBAL slow_query_log = ON;

-- 设置阈值 1 秒
SET GLOBAL long_query_time = 1;

-- 记录未使用索引的查询
SET GLOBAL log_queries_not_using_indexes = ON;

-- 设置日志文件路径
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow-query.log';

-- 仅记录扫描行数超过 1000 的查询
SET GLOBAL min_examined_row_limit = 1000;

3.3.2 Redis 慢查询日志配置

Redis 的慢查询日志机制与 MySQL 不同,它记录的是执行时间超过指定微秒数的命令:

参数名 默认值 推荐值 说明
slowlog-log-slower-than 10000(10ms) 1000(1ms) 超过此微秒数的命令被记录
slowlog-max-len 128 1024 最多保留的慢查询条目数

配置示例(需写入 redis.conf 并重启):

# redis.conf
slowlog-log-slower-than 1000
slowlog-max-len 1024

查看慢查询日志:

# 查看最近 10 条慢查询
SLOWLOG GET 10

# 查看慢查询条目数
SLOWLOG LEN

# 清空慢查询日志
SLOWLOG RESET

3.3.3 慢查询日志解读

以下是一个典型的 MySQL 慢查询日志条目:

# Time: 2025-03-15T10:23:45.123456Z
# User@Host: report_user[report_user] @  [10.0.1.100]
# Query_time: 12.345678  Lock_time: 0.001234  Rows_sent: 5000  Rows_examined: 2000000
SET timestamp=1742029425;
SELECT
  o.order_id, o.order_date, o.amount, c.customer_name
FROM orders o
  LEFT JOIN customers c ON o.customer_id = c.id
WHERE o.order_date BETWEEN '2025-01-01' AND '2025-03-15'
ORDER BY o.order_date DESC;

关键字段解读:

  • Query_time:查询总耗时(12.34 秒),远超阈值
  • Lock_time:锁等待时间(1.23 毫秒),占比很小
  • Rows_sent:返回行数(5000 行),正常
  • Rows_examined:扫描行数(200 万行),远大于返回行数,索引缺失的典型信号
  • SQL 语句:涉及 LEFT JOIN 和范围查询,order_date 和 customer_id 很可能缺少联合索引
排查口诀: 扫描行数远大于返回行数 → 索引缺失;锁等待时间占比高 → 锁竞争;扫描行数正常但执行慢 → 数据量大或网络延迟。
慢查询排查流程 触发慢查询 获取慢查询日志 分析关键指标 判断慢查询原因 索引缺失 数据量大 锁竞争