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很可能缺少联合索引
排查口诀: 扫描行数远大于返回行数 → 索引缺失;锁等待时间占比高 → 锁竞争;扫描行数正常但执行慢 → 数据量大或网络延迟。