06.3 - 慢查询与性能诊断
定位: Redis 生产环境的性能监控与问题排查方法 面试高频度: ⭐⭐⭐⭐ 考查方式: 延迟排查思路、SLOWLOG 使用、大 key 诊断、热点 key 发现
一、慢查询日志
Redis 自动记录执行时间超过阈值的命令:
配置:
slowlog-log-slower-than 10000 # 单位微秒,10ms
slowlog-max-len 128 # 保留最近 128 条
使用:
SLOWLOG GET 10 # 查看最近 10 条慢查询
SLOWLOG LEN # 查看慢查询总数
SLOWLOG RESET # 清空慢查询
慢查询输出示例:
127.0.0.1:6379> SLOWLOG GET 1
1) 1) (integer) 123 # 唯一 ID
2) (integer) 1678901234 # Unix 时间戳
3) (integer) 45678 # 耗时(微秒)= 45.678ms
4) 1) "KEYS" # 命令
2) "user:*" # 参数
5) "127.0.0.1:12345" # 客户端
6) "my-application" # 客户端名称二、延迟排查
2.1 redis-cli 自带延迟工具
bash
# 延迟监控
redis-cli --latency # 持续显示延迟
# min: 0, max: 1, avg: 0.12 (1079 samples)
redis-cli --latency-history # 每 15 秒报告一次
# min: 0, max: 3, avg: 0.15 (14372 samples) -- 15.01 seconds
redis-cli --latency-dist # 延迟分布柱状图(可视化)
# 内置延迟测试(服务端记录)
redis-cli DEBUG SLEEP 0.5 # 模拟 500ms 延迟(会阻塞!)
redis-cli --latency
# 你会看到 max 跳到 500ms 以上2.2 延迟来源
延迟来源排查清单:
网络延迟:
redis-cli --latency -h <remote_ip>,如果 avg > 1ms,可能是网络问题慢命令(阻塞事件循环):KEYS *、SMEMBERS、HGETALL、ZRANGE、LRANGE 都是 O(N) 操作!替代:SCAN/SSCAN/HSCAN/ZSCAN 分批
Fork 阻塞:BGSAVE / BGREWRITEAOF → fork(),大内存实例 fork 可能阻塞 100-500ms,所有请求在这段时间等待。缓解:只在从节点做持久化
写时复制(COW)内存翻倍:bgsave 期间大量写入 → COW 复制 → 内存翻倍 → swap → 延迟暴增。监控:rdb_last_cow_size
三、热点 key 发现
热点 key = 被大量请求集中访问的 key
问题:
一个 key 每秒被访问 10 万次
→ 该命令排队执行
→ 虽然 Redis 单机 10 万 QPS 没问题
→ 但这个 key 的访问占据了大部分 CPU
发现方法:
方法 1:redis-cli --hotkeys(Redis 4.0+)
redis-cli --hotkeys
# 扫描并统计访问频率最高的 key
# 需要启用 LFU 策略
方法 2:MONITOR 抽样
redis-cli MONITOR | head -10000 | awk '{print $4}' | sort | uniq -c | sort -nr | head -10
# 统计 TOP 10 访问的 key
方法 3:客户端自定义
在 Redis 客户端统计每个 key 的访问次数
(最好在客户端做,不增加 Redis 负担)
解决热点 key:
✓ 本地缓存(Caffeine/Guava Cache)——热点 key 在应用内存缓存
✓ 读写分离(从节点分担读请求)
✓ 多个副本(不同 key 名,random 路由)
✓ Hash Tag 拆分(一个大 key 拆成多个 key)四、实战验证
4.1 慢查询操作
bash
# 1. 配置慢查询阈值(100ms)
redis-cli CONFIG SET slowlog-log-slower-than 100000
# 2. 执行一个慢命令
redis-cli KEYS "*" # 全量扫描(在大量 key 时很慢)
# 3. 查看慢查询
redis-cli SLOWLOG GET 5
# 1) 1) (integer) 1
# 2) (integer) 1678901234
# 3) (integer) 152345 # 152ms!
# 4) 1) "KEYS"
# 2) "*"
# 4. 查看慢查询统计
redis-cli SLOWLOG LEN
# (integer) 14.2 延迟诊断
bash
# 1. 基准延迟测试
redis-cli --latency -h 127.0.0.1 -p 6379
# min: 0, max: 2, avg: 0.11
# 2. 模拟大 key 对延迟的影响
redis-cli SET bigkey "$(dd if=/dev/zero bs=1M count=10 2>/dev/null | base64)"
redis-cli --latency &
redis-cli GET bigkey > /dev/null # 在另一个窗口执行
# ← 延迟从 avg 0.11ms 跳到 max 85ms
# 3. 模拟慢命令
redis-cli --latency &
redis-cli DEBUG SLEEP 2 # 另一个窗口:模拟 2 秒阻塞
# ← 延迟跳到 2000ms(所有请求被阻塞 2 秒)
# 4. 查看服务器内部延迟统计
redis-cli INFO commandstats
# cmdstat_get:calls=100,usec=500,usec_per_call=5.00
# cmdstat_set:calls=200,usec=800,usec_per_call=4.00
# cmdstat_keys:calls=1,usec=152345,usec_per_call=152345.00 ← 这个太慢了!4.3 连接和内存监控
bash
# 当前连接数
redis-cli CLIENT LIST | wc -l
redis-cli INFO clients
# connected_clients:50
# client_longest_output_list:0
# 观察阻塞的客户端
redis-cli CLIENT LIST | grep -E "obl|oll"
# obl: 输出缓冲区长度
# oll: 输出链表长度
# 查看内存状态
redis-cli INFO memory
# evicted_keys: 1000 ← 被淘汰的 key 数量(如果 >0 说明内存不够)
# keyspace_hits: 100000
# keyspace_misses: 500
# 命中率 ≈ 99.5%4.4 热 key 排查
bash
# MONITOR 方法(生产谨慎使用,会降低 50% 性能!)
redis-cli MONITOR | head -1000 | grep -v "PING" | awk '{print $4}' | sort | uniq -c | sort -nr | head -10
# --hotkeys 方法(安全,需要 LFU 策略)
redis-cli --hotkeys
# 观察 key 的过期频率
redis-cli INFO stats | grep expired_keys
# expired_keys: 5000 ← 如果很高,检查过期时间配置五、面试视角
| 追问 | 答案要点 |
|---|---|
| 怎么排查 Redis 延迟突增? | ① redis-cli --latency 看基准 → ② SLOWLOG 看慢命令 → ③ 网络 ping → ④ 检查 fork/fragmentation |
| KEYS * 为什么不建议用? | O(N) 阻塞单线程,1000 万 key → 阻塞几十秒。用 SCAN 代替 |
| 什么是慢查询阈值? | slowlog-log-slower-than 微秒,超过的记录到 slowlog;不限制执行,只记录日志 |
| MONITOR 命令的代价? | 会降低 Redis 50%+ 的性能,仅调试用,生产慎用 |
| 热点 key 怎么解决? | 本地缓存 + 副本 key + 读写分离 + 拆分 |
| 如何监控内存不足? | evicted_keys > 0 表示开始淘汰,used_memory 接近 maxmemory 时预警 |
| INFO commandstats 的作用? | 查看每个命令的调用次数、总耗时和平均耗时,发现耗时高的命令 |
📚 相关链接
- **内存优化** — 内存问题的诊断与解决
- **Pipeline** — 性能优化的重要手段
- **AOF** — AOF 阻塞的诊断
- ← 返回 **性能优化与运维索引**