4、慢查询日志实战:模拟生成慢查询、查看慢查询日志、分析日志中的耗时与命令
慢查询日志是 Redis 性能诊断的第一手现场证据。本章通过完整的实战流程,从模拟慢查询、配置日志参数、查看日志内容,到逐行分析耗时与命令特征,帮助运维人员建立从“现象”到“根因”的闭环分析能力。
4.1 模拟慢查询:构造延迟场景
在生产环境中,慢查询通常由 大 Key 操作、复杂聚合命令 或 批量写入 触发。为了在实验环境中复现,我们使用 DEBUG SLEEP 命令人为制造延迟,该命令会使当前客户端连接阻塞指定的秒数,从而被慢查询日志捕获。
实验环境准备:确保 Redis 实例的
slowlog-log-slower-than 设置为较低阈值(如 10000 微秒 = 10 毫秒),以便捕获人为制造的延迟。
模拟慢查询步骤:
- 连接 Redis 实例:
redis-cli -h 127.0.0.1 -p 6379 - 执行一个耗时 2 秒的阻塞命令:
DEBUG SLEEP 2 - 执行一个耗时 5 秒的阻塞命令:
DEBUG SLEEP 5 - 执行一个正常命令作为对照:
SET key "hello" - 执行一个模拟大集合操作的命令:
SADD myset a b c d e f g h i j
127.0.0.1:6379> DEBUG SLEEP 2
OK
(2.00s)
127.0.0.1:6379> DEBUG SLEEP 5
OK
(5.00s)
127.0.0.1:6379> SET key "hello"
OK
127.0.0.1:6379> SADD myset a b c d e f g h i j
(integer) 10
关键点:
DEBUG SLEEP 仅在测试环境使用;生产环境应通过真实的大 Key 扫描或慢查询复现来模拟。
4.2 查看慢查询日志:SLOWLOG 命令家族
Redis 提供了 SLOWLOG 命令组来管理慢查询日志,包括查看、计数和重置。日志存储在内存中,默认最多保留 128 条,超过后最旧的记录会被丢弃。
核心命令速查表:
| 命令 | 功能 | 示例 |
|---|---|---|
SLOWLOG GET [n] |
获取最近 n 条慢查询记录(默认 10 条) | SLOWLOG GET 5 |
SLOWLOG LEN |
获取当前慢查询日志的总条数 | SLOWLOG LEN |
SLOWLOG RESET |
清空所有慢查询日志 | SLOWLOG RESET |
查看慢查询日志:
127.0.0.1:6379> SLOWLOG GET 3
1) 1) (integer) 2
2) (integer) 1712345678
3) (integer) 5000000
4) 1) "DEBUG"
2) "SLEEP"
3) "5"
5) "127.0.0.1:6379"
6) "user=default"
2) 1) (integer) 1
2) (integer) 1712345676
3) (integer) 2000000
4) 1) "DEBUG"
2) "SLEEP"
3) "2"
5) "127.0.0.1:6379"
6) "user=default"
3) 1) (integer) 0
2) (integer) 1712345670
3) (integer) 12000
4) 1) "SADD"
2) "myset"
3) "a"
4) "b"
5) "c"
6) "d"
7) "e"
8) "f"
9) "g"
10) "h"
11) "i"
12) "j"
5) "127.0.0.1:6379"
6) "user=default"
4.3 日志字段深度解析
每条慢查询日志记录包含 6 个字段,理解每个字段的含义是分析的基础。
| 字段索引 | 字段含义 | 示例值 | 分析要点 |
|---|---|---|---|
| 1 | 日志唯一 ID(自增) | 2 |
ID 越大表示记录越新 |
| 2 | Unix 时间戳(秒) | 1712345678 |
可转换为可读时间,用于定位故障时间窗口 |
| 3 | 命令执行耗时(微秒) | 5000000 |
核心指标,5 秒 = 5,000,000 微秒 |
| 4 | 命令及其参数(数组) | ["DEBUG","SLEEP","5"] |
识别具体操作,如大 Key 遍历、聚合计算 |
| 5 | 客户端 IP 和端口 | "127.0.0.1:6379" |
定位来源应用或连接池 |
| 6 | 客户端名称(需设置) | "user=default" |
用于区分不同业务线 |
注意:耗时字段的单位是微秒(1 秒 = 1,000,000 微秒)。在分析时,建议将阈值设置为 10,000 微秒(10 毫秒)以上,避免日志过多影响性能。
4.4 日志分析:从耗时到根因
分析慢查询日志的核心思路是:先看耗时,再看命令,最后关联上下文。以下是对上述模拟日志的分析过程。
分析流程:
- 排序与筛选:按耗时降序排列,优先处理最慢的查询。本例中
DEBUG SLEEP 5耗时 5 秒,为最严重记录。 - 命令识别:识别耗时命令的类型。
DEBUG SLEEP是测试命令,生产环境应关注KEYS、SMEMBERS、HGETALL、LRANGE、ZRANGE等可能遍历大 Key 的命令。 - 参数分析:检查命令参数是否涉及大量元素。例如
SADD myset a b c ...虽然只添加了 10 个元素,但如果集合本身巨大,SADD也可能变慢。 - 客户端定位:根据客户端 IP 和名称,找到对应的应用服务或模块。
实战技巧:使用
SLOWLOG GET 100 导出最近 100 条日志,结合 awk 或 jq 进行批量分析,统计耗时分布和命令频率。
4.5 慢查询日志分析流程图
下图展示了从日志采集到根因定位的完整分析链路,帮助运维人员建立系统化的排查思维。
4.6 慢查询日志配置与优化建议
合理配置慢查询参数,既能捕获问题,又不会对 Redis 性能产生负面影响。
| 配置项 | 默认值 | 推荐值 | 说明 |
|---|---|---|---|
slowlog-log-slower-than |
10000(10 毫秒) | 10000 ~ 20000 | 生产环境建议 10 毫秒,高并发场景可放宽至 20 毫秒 |
slowlog-max-len |
128 | 1024 | 增加日志容量,避免高频慢查询覆盖历史记录 |
配置生效方式:使用
CONFIG SET 动态调整,无需重启 Redis。例如:CONFIG SET slowlog-max-len 1024。若需持久化,需写入 redis.conf 文件。
4.7 常见慢查询命令与优化方向
根据生产环境统计,以下命令最容易成为慢查询的源头,需要重点监控。
| 命令 | 慢查询原因 | 优化方向 |
|---|---|---|
KEYS * |
全库遍历,时间复杂度 O(N) | 使用 SCAN 替代,或维护索引集合 |
SMEMBERS |
返回集合所有元素,大 Key 时耗时剧增 | 使用 SSCAN 分批读取,或拆分集合 |
HGETALL |
返回哈希所有字段,大 Hash 时内存和网络开销大 | 使用 HSCAN 或只获取必要字段 |
LRANGE |
返回列表指定范围元素,范围过大时耗时增加 | 限制返回条数,使用分页或裁剪列表 |
ZRANGE |
返回有序集合指定范围元素,大集合时性能下降 | 使用 ZSCAN 或限制范围大小 |
SORT |
排序操作涉及多 Key 读取和计算 | 避免在 Redis 中排序,交由应用层处理 |
生产警示:绝对不要在线上环境执行
KEYS * 或 FLUSHALL 等危险命令。建议通过 RENAME_COMMAND 配置重命名或禁用这些命令。
4.8 慢查询日志分析工具与脚本
对于大量慢查询日志,手动逐条分析效率低下。推荐使用以下方法进行批量处理。
Shell 脚本示例:统计耗时分布
#!/bin/bash
# 导出慢查询日志并统计耗时区间
redis-cli SLOWLOG GET 1000 | awk '
/^3\) \(integer\)/ {
time_us = $3
gsub(/[^0-9]/, "", time_us)
if (time_us < 10000) print "<10ms"
else if (time_us < 100000) print "10-100ms"
else if (time_us < 1000000) print "100ms-1s"
else print ">1s"
}' | sort | uniq -c | sort -rn
Python 脚本示例:解析日志并输出 Top 10 慢命令
import redis
import time
r = redis.Redis(host='127.0.0.1', port=6379, decode_responses=True)
logs = r.slowlog_get(100)
# 按耗时排序
sorted_logs = sorted(logs, key=lambda x: x['duration'], reverse=True)
print(f"{'排名':<4} {'耗时(ms)':<10} {'命令':<20} {'参数':<30}")
print("="*64)
for i, log in enumerate(sorted_logs[:10], 1):
cmd = log['args'][0] if log['args'] else 'N/A'
args = ' '.join(log['args'][1:4]) if len(log['args']) > 1 else ''
duration_ms = log['duration'] / 1000
print(f"{i:<4} {duration_ms:<10.2f} {cmd:<20} {args:<30}")
最佳实践:将慢查询日志接入监控系统(如 Prometheus + Grafana),设置耗时告警阈值,实现自动化发现与通知。