1. 项目背景
1.1 业务场景
电商系统在接入 Redis 后的第二周,告警群开始热闹起来。周一上午 10 点,商品详情接口大面积超时,P99 延迟从 5ms 涨到 2.3 秒。运维同学第一反应是"Redis 挂了",SSH 登录后发现进程还在、CPU 也不高。但 INFO stats 显示 instantaneous_ops_per_sec 从 25000 掉到了 3000。排查了 20 分钟后才发现——有人在业务高峰期执行了 KEYS *product*,Redis 主线程被阻塞了 2.3 秒,所有接口都在排队。
周三凌晨 3 点,Redis 内存打满触发 OOM,写入全部失败(maxmemory-policy 设为了 noeviction)。缓存全部 miss,数据库瞬间被回源流量打满。凌晨 4 点,运维重启 Redis 后才发现——maxmemory 配置的是 8GB,但业务团队在活动期间塞了大量无 TTL 的大 key,实际需要 12GB。
周五下午,测试环境的 FLUSHALL 误清了预发布环境的 key(因为共用了一个 Redis 实例的 db 编号隔离),导致预发布所有缓存 miss,触发了一次假故障。
这些问题的共同特征——不是 Redis 代码有 bug,而是使用方式、配置策略、运维流程有缺陷。
1.2 排障的基本原则
| 原则 | 说明 |
|---|---|
| 不要先重启 | 重启会清空现场——慢查询日志、连接状态、内存快照全没了 |
| 先看 INFO | INFO 是 Redis 的"体检报告",不同段(server/clients/memory/stats/persistence/replication)各有价值 |
| 对比基线 | 正常时各项指标是多少?出问题时偏离多少?没有基线,排查就是猜谜 |
| 从外到内 | 先确认网络通不通→认证对不对→命令有没有执行→有没有慢命令→有没有大 key |
| 保持现场 | 出问题时先保存 INFO ALL、CLIENT LIST、SLOWLOG GET 128、CONFIG GET * |
1.3 本章目标
建立 Redis 日常运维和基础故障排查的五个核心能力:
- 读懂
INFO的关键指标并建立基线。 - 使用
CLIENT LIST排查连接问题。 - 用
SLOWLOG和LATENCY定位性能瓶颈。 - 发现和处理大 key、热 key、无 TTL key。
- 安全地执行备份、重启和配置变更。
2. 项目设计
2.1 第一幕:Redis 又抽风了,重启一下?
小胖(盯着告警群):"Redis 又不行了!接口大面积超时,要不重启一下?重启治百病。"
小白:"等一下!先别重启。重启会丢掉重要的诊断信息——比如是谁在执行慢命令、内存里哪些 key 最大、哪个客户端连接数异常——这些信息一旦重启就没了。而且如果问题不在 Redis(比如是网络抖动或应用连接池配置问题),重启完全没用。"
大师:"排障就像医生看病。你不做检查就直接开刀,病人可能死在手术台上。Redis 有丰富的诊断命令,先用它们收集证据。"
Redis 排障速查卡:
1. PING → 能通吗?
↓
2. INFO server → 版本、运行时间(最近重启过吗?)
↓
3. INFO clients → 连接数正常吗?有没有异常客户端?
↓
4. INFO memory → 内存用到了多少?有没有在淘汰 key?
↓
5. INFO stats → QPS 正常吗?命中率有没有暴跌?
↓
6. SLOWLOG GET 20 → 最近有慢命令吗?
↓
7. CLIENT LIST → 哪些客户端在连接?有没有异常?
↓
8. LATENCY DOCTOR → 有没有延迟尖刺?
技术映射:先诊断,再治疗。INFO 是体检报告,SLOWLOG 是病历,CLIENT LIST 是探视记录。把证据收齐再动刀。
2.2 第二幕:慢查询日志没有记录,但应用仍然超时
小胖:"我用 SLOWLOG GET 看了,最近 10 条都是微秒级的,没有慢命令。但应用还是超时啊!"
小白:"这说明瓶颈可能不在 Redis 内部,而在 Redis 外部。客户端连接池耗尽了、网络延迟过高、应用 GC 停顿——这些 Redis 的慢查询日志都看不到。"
大师:"慢查询记录的是命令在 Redis 内部执行的时间,不包括网络传输时间、客户端排队时间和应用处理时间。所以排查性能问题要全链路看:"
应用超时可能的原因分布:
┌─────────────────────────────────────────────┐
│ 应用层(30%) │
│ - 线程池/连接池耗尽 │
│ - GC 停顿 │
│ - 序列化/反序列化慢 │
├─────────────────────────────────────────────┤
│ 网络层(20%) │
│ - 跨机房网络延迟 │
│ - 防火墙/负载均衡瓶颈 │
│ - TCP 重传 │
├─────────────────────────────────────────────┤
│ Redis 客户端层(15%) │
│ - 连接池配置不当(maxTotal/maxIdle) │
│ - Pipeline 大包阻塞 │
│ - 集群重定向过多 │
├─────────────────────────────────────────────┤
│ Redis 服务端层(35%) │
│ - 慢命令阻塞 │
│ - fork 子进程(BGSAVE 时) │
│ - 内存淘汰 │
│ - 大 key 删除 │
└─────────────────────────────────────────────┘
技术映射:SLOWLOG 只是 Redis 视角的慢,不等于全链路的慢。排查时要从应用监控(APM)、网络监控和 Redis 监控三个维度交叉对比。
2.3 第三幕:大 key 的发现与处理
小胖:"怎么知道哪个 key 最大?"
大师:"有几个工具可以用。redis-cli --bigkeys 可以扫描所有 key 按类型统计最大的。MEMORY USAGE key 可以精确计算某个 key 占用内存。还有 STRLEN(String 长度)、HLEN(Hash 字段数)、LLEN(List 长度)、SCARD(Set 成员数)、ZCARD(ZSet 成员数)分别看各类型的规模。"
# 用 redis-cli 内置工具扫描大 key
redis-cli -a redis123 --bigkeys
# 输出示例:
# [00.00%] Biggest string found so far '"mall:cache:product:..."' with 25600 bytes
# [05.14%] Biggest hash found so far '"mall:cart:user:9527"' with 382 fields
# ...
# -------- summary -------
# Sampled 8245 keys in the keyspace!
# Total key length in bytes is 235678
# Biggest string found '"mall:cache:product:8888"' has 45230 bytes
# Biggest hash found '"mall:cart:user:1001"' has 1250 fields
大师:"发现大 key 之后的处理要谨慎。删除大 key 不要用 DEL——它会在主线程同步释放内存。用 UNLINK,它在后台线程异步释放。如果必须用 DEL,要选择业务低峰期。"
# 危险!大 key 同步删除 → 阻塞主线程
DEL mall:big:hash
# 安全!异步删除 → 不阻塞主线程
UNLINK mall:big:hash
# 渐进式删除大 Hash(每次都删一小部分)
# 用 HSCAN + HDEL 分批删除,每批 100 个 field
3. 项目实战
3.1 环境准备
docker exec -it redis-lab redis-cli -a redis123
3.2 实战一:INFO 关键指标解读
# 服务器信息
127.0.0.1:6379> INFO server
# redis_version:8.6.0 ← 确认版本
# uptime_in_seconds:86400 ← 运行了多久(重启过吗?)
# process_id:1 ← 容器内 PID
# 客户端信息
127.0.0.1:6379> INFO clients
# connected_clients:5 ← 当前连接数
# maxclients:10000 ← 最大连接数配置
# client_recent_max_input_buffer:0 ← 输入缓冲区
# client_recent_max_output_buffer:0 ← 输出缓冲区
# 内存信息
127.0.0.1:6379> INFO memory
# used_memory_human:45.23M ← Redis 实际使用内存
# used_memory_rss_human:52.10M ← 操作系统视角(含碎片)
# maxmemory_human:512.00M ← 配置的最大内存
# mem_fragmentation_ratio:1.15 ← 碎片率 = rss/used
# (> 1.5 说明碎片严重,考虑 active defrag)
# 统计信息
127.0.0.1:6379> INFO stats
# total_commands_processed:1234567
# instantaneous_ops_per_sec:8240 ← 当前 QPS
# keyspace_hits:152340 ← 缓存命中
# keyspace_misses:12890 ← 缓存未命中
# expired_keys:156 ← 过期删除数
# evicted_keys:0 ← 淘汰删除数 (如果>0要警惕!)
# rejected_connections:0 ← 连接拒绝数
# 持久化信息
127.0.0.1:6379> INFO persistence
# rdb_last_bgsave_status:ok ← 最近 RDB 是否成功
# aof_enabled:1
# aof_last_write_status:ok
# 键空间信息
127.0.0.1:6379> INFO keyspace
# db0:keys=8245,expires=3200,avg_ttl=452000
关键告警阈值建议:
| 指标 | 正常范围 | 告警阈值 | 紧急阈值 |
|---|---|---|---|
mem_fragmentation_ratio |
< 1.5 | > 2.0 | > 3.0 |
evicted_keys 增长率 |
0 | > 10/s | > 100/s |
connected_clients |
基线±20% | 超过 maxclients 的 70% | 超过 90% |
instantaneous_ops_per_sec |
基线±30% | 骤降 50% | 骤降 80% |
rejected_connections |
0 | > 0 | > 10/s |
3.3 实战二:CLIENT LIST 排查连接问题
# 查看所有客户端连接
127.0.0.1:6379> CLIENT LIST
# id=3 addr=127.0.0.1:54321 laddr=127.0.0.1:6379 fd=8 name= age=120 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 argv-mem=0 obl=0 oll=0 omem=0 tot-mem=2048 events=r cmd=client|list user=default redir=-1 resp=3
# 关键字段解读:
# id: 客户端 ID
# addr: 客户端地址 (IP:端口)
# age: 连接已建立的秒数
# idle: 最后一次执行命令距今的秒数
# flags: N=普通客户端, b=阻塞中(如 BLPOP), M=master, S=slave
# cmd: 最后一次执行的命令
# qbuf: 查询缓冲区大小(命令未完整接收)
# obl/oll: 输出缓冲区长度(固定/动态)
# omem: 输出缓冲区占用内存
# 查看客户端统计
127.0.0.1:6379> CLIENT INFO
排查连接问题的典型场景:
# 场景1: 连接数突增 → 检查是否有应用泄漏连接
# 按 addr 统计各 IP 的连接数
127.0.0.1:6379> CLIENT LIST | grep "10.0.1.5"
# 场景2: 某客户端输出缓冲区超大 → 可能是慢消费(如 Monitor 客户端)
# 看 omem 字段
127.0.0.1:6379> CLIENT LIST | grep "omem="
# 场景3: 大量 idle 连接 → 可能连接池配置了保活但无复用
# 看 idle 字段,闲置几百秒的可能需要清理
# 杀掉指定客户端
127.0.0.1:6379> CLIENT KILL 127.0.0.1:54321
# 或按条件杀
127.0.0.1:6379> CLIENT KILL SKIPME no IDLE 300 # 杀闲置超过 300 秒的
3.4 实战三:SLOWLOG 慢查询分析
# 配置慢查询
127.0.0.1:6379> CONFIG SET slowlog-log-slower-than 10000 # 超过 10ms 记录
OK
127.0.0.1:6379> CONFIG SET slowlog-max-len 128
OK
127.0.0.1:6379> SLOWLOG RESET
# 模拟一些慢操作
127.0.0.1:6379> KEYS *
127.0.0.1:6379> EVAL "local sum=0; for i=1,100000 do sum=sum+i end; return sum" 0
# 查看慢查询日志
127.0.0.1:6379> SLOWLOG GET 5
# 1) 1) (integer) 1 # 日志唯一 ID
# 2) (integer) 1714500000 # 执行时间戳
# 3) (integer) 231000 # 执行耗时(微秒)→ 231ms!
# 4) 1) "KEYS" # 命令
# 2) "*"
# 5) "127.0.0.1:54321" # 客户端地址
# 6) "" # 客户端名称
# 慢查询日志长度
127.0.0.1:6379> SLOWLOG LEN
(integer) 2
# 分析慢查询趋势(需要多次采样)
# 关注: 哪些命令最慢、什么时间段集中、是否和业务高峰吻合
3.5 实战四:大 key 发现与处理
# 方法 1: 用 --bigkeys 参数扫描
# 退出 redis-cli,在容器内执行:
docker exec redis-lab redis-cli -a redis123 --bigkeys
# 方法 2: 手动检查单个 key 的大小
127.0.0.1:6379> MEMORY USAGE mall:cache:product:1001
(integer) 312
127.0.0.1:6379> STRLEN mall:cache:product:1001
(integer) 245
# 方法 3: 检查集合类 key 的规模
127.0.0.1:6379> LLEN mall:ops:latest
(integer) 15000 # 列表有 15000 个元素 → 偏大
127.0.0.1:6379> HLEN mall:cart:user:9527
(integer) 382 # Hash 有 382 个字段 → 正常
127.0.0.1:6379> ZCARD mall:rank:point
(integer) 250000 # ZSet 有 25 万成员 → 偏大
# 大 key 删除的正确姿势:
# DEL → 同步删除,可能阻塞 → 不推荐
# UNLINK → 异步删除,不阻塞 → 推荐
127.0.0.1:6379> UNLINK mall:ops:latest
(integer) 1
3.6 实战五:延迟诊断
# LATENCY DOCTOR: 综合诊断报告
127.0.0.1:6379> LATENCY DOCTOR
# 输出各延迟事件的分析和建议
# LATENCY LATEST: 最近的延迟事件
127.0.0.1:6379> LATENCY LATEST
# 返回各种延迟事件类型:
# command: 命令执行延迟
# fast-command: 快命令异常慢
# fork: fork 子进程时延迟
# LATENCY HISTORY event: 特定事件的延迟历史
127.0.0.1:6379> LATENCY HISTORY command
# LATENCY RESET: 重置延迟统计
127.0.0.1:6379> LATENCY RESET
3.7 实战六:模拟 5 类常见故障
故障 1:认证失败
# 无密码连接
127.0.0.1:6379> PING
(error) NOAUTH Authentication required.
# 解决: AUTH redis123
127.0.0.1:6379> AUTH redis123
OK
127.0.0.1:6379> PING
PONG
故障 2:类型错误
127.0.0.1:6379> SET mall:user:1001 "string-value"
OK
127.0.0.1:6379> HGET mall:user:1001 name
(error) WRONGTYPE Operation against a key holding the wrong kind of value
# 诊断: TYPE 确认类型
127.0.0.1:6379> TYPE mall:user:1001
string
# → 应该用 GET 而不是 HGET。或者 key 被错误覆盖了
故障 3:OOM(内存满)
# 设置很小的 maxmemory 模拟 OOM
127.0.0.1:6379> CONFIG SET maxmemory 1mb
OK
# 尝试写入大 value
127.0.0.1:6379> SET large:key "lots of data..."
(error) OOM command not allowed when used memory > 'maxmemory'.
# 诊断:
127.0.0.1:6379> INFO memory | grep "used_memory_human\|maxmemory_human\|evicted_keys"
# 如果 evicted_keys=0 且报 OOM → maxmemory-policy 可能是 noeviction
127.0.0.1:6379> CONFIG GET maxmemory-policy
1) "maxmemory-policy"
2) "noeviction" # ← 不淘汰只拒绝写入!
# 解决: 改淘汰策略
127.0.0.1:6379> CONFIG SET maxmemory-policy allkeys-lru
OK
# 恢复 maxmemory
127.0.0.1:6379> CONFIG SET maxmemory 512mb
OK
故障 4:慢查询定位
# 执行一次 KEYS(学习环境模拟,生产不要!)
127.0.0.1:6379> KEYS mall:*
# ... 返回结果 ...
# 查看慢查询
127.0.0.1:6379> SLOWLOG GET 1
# 可以看到 KEYS 的耗时
故障 5:大 key 阻塞删除
# 创建一个大 List 模拟
127.0.0.1:6379> DEL big:list
(integer) 1
# ... 写入 50000 个元素 ...
127.0.0.1:6379> LLEN big:list
(integer) 50000
# 危险删除(同步)
# DEL big:list ← 可能阻塞几百毫秒
# 安全删除(异步)
127.0.0.1:6379> UNLINK big:list
(integer) 1
3.8 实战七:备份与恢复检查
# 查看持久化状态
127.0.0.1:6379> INFO persistence
# rdb_last_save_time: 上次 RDB 保存时间
# rdb_changes_since_last_save: 上次保存以来的变更数
# 手动触发后台快照
127.0.0.1:6379> BGSAVE
Background saving started
# 查看最后一次 BGSAVE 是否成功
127.0.0.1:6379> LASTSAVE
(integer) 1714500000
# 查看 AOF 状态
127.0.0.1:6379> CONFIG GET appendonly
127.0.0.1:6379> CONFIG GET appendfsync
3.9 实战八:监控指标采集脚本
日常运维需要定时采集关键指标,形成趋势图。以下是一个 Python 脚本示例,可集成到定时任务中:
import redis
import json
import time
def collect_redis_metrics(host='localhost', port=6379, password='redis123'):
r = redis.Redis(host=host, port=port, password=password, decode_responses=True)
metrics = {}
# 基础信息
info_server = r.info('server')
metrics['uptime_seconds'] = info_server.get('uptime_in_seconds', 0)
metrics['redis_version'] = info_server.get('redis_version', 'unknown')
# 客户端
info_clients = r.info('clients')
metrics['connected_clients'] = info_clients.get('connected_clients', 0)
metrics['blocked_clients'] = info_clients.get('blocked_clients', 0)
metrics['maxclients'] = info_clients.get('maxclients', 10000)
# 内存
info_memory = r.info('memory')
used_memory = info_memory.get('used_memory', 0)
maxmemory = info_memory.get('maxmemory', 0)
metrics['used_memory_bytes'] = used_memory
metrics['maxmemory_bytes'] = maxmemory
metrics['memory_usage_pct'] = round(used_memory / maxmemory * 100, 2) if maxmemory > 0 else 0
metrics['mem_fragmentation_ratio'] = info_memory.get('mem_fragmentation_ratio', 1.0)
# 统计
info_stats = r.info('stats')
metrics['ops_per_sec'] = info_stats.get('instantaneous_ops_per_sec', 0)
metrics['keyspace_hits'] = info_stats.get('keyspace_hits', 0)
metrics['keyspace_misses'] = info_stats.get('keyspace_misses', 0)
metrics['expired_keys'] = info_stats.get('expired_keys', 0)
metrics['evicted_keys'] = info_stats.get('evicted_keys', 0)
# 计算命中率
total = metrics['keyspace_hits'] + metrics['keyspace_misses']
metrics['hit_rate'] = round(metrics['keyspace_hits'] / total * 100, 2) if total > 0 else 0
# 持久化
info_pers = r.info('persistence')
metrics['rdb_last_save_seconds_ago'] = time.time() - info_pers.get('rdb_last_save_time', 0)
metrics['rdb_changes_since_last_save'] = info_pers.get('rdb_changes_since_last_save', 0)
# 慢查询
slowlog = r.slowlog_get(10)
metrics['slowlog_count'] = len(slowlog)
if slowlog:
metrics['slowlog_max_duration_us'] = max(s[2] for s in slowlog)
return metrics
# 输出 JSON 格式,方便 Prometheus/Grafana 采集
if __name__ == '__main__':
metrics = collect_redis_metrics()
print(json.dumps(metrics, indent=2))
3.10 实战九:连接池配置检查
很多"Redis 超时"问题的根源不在 Redis,而在客户端的连接池配置:
// 典型的 Lettuce 连接池配置(Spring Boot)
@Configuration
public class RedisConfig {
@Bean
public LettuceClientConfigurationBuilderCustomizer customizer() {
return builder -> builder
.clientOptions(ClientOptions.builder()
.autoReconnect(true) // 自动重连
.disconnectedBehavior(DisconnectedBehavior.REJECT_COMMANDS)
.socketOptions(SocketOptions.builder()
.connectTimeout(Duration.ofSeconds(1)) // 连接超时
.keepAlive(true)
.build())
.build())
.commandTimeout(Duration.ofMillis(500)); // 命令超时
}
}
连接池常见参数及其排查意义:
| 参数 | 建议值 | 如果太小 | 如果太大 |
|---|---|---|---|
| maxTotal | 50-200 | 高并发时线程等待连接,接口超时 | 占用 Redis 连接数,可能触发 maxclients |
| maxIdle | 10-50 | 频繁创建/销毁连接 | 连接数浪费 |
| minIdle | 5-10 | 突发流量时建立连接慢 | 同上 |
| commandTimeout | 500-2000ms | 正常请求也超时 | 慢命令无法及时发现 |
| maxWaitMillis | 2000ms | 线程快速放弃,大量超时日志 | 线程堆积 |
3.11 实战十:热 key 发现与处理
热 key 是指被大量请求集中访问的 key,可能成为 Redis 的性能瓶颈:
# 方法 1: redis-cli --hotkeys(Redis 4.0+)
docker exec redis-lab redis-cli -a redis123 --hotkeys
# 输出示例:
# Hot key 'mall:cache:product:8888' found with 15000 hits (占 23.5%)
# 方法 2: MONITOR + 统计(低峰期临时使用,生产慎用!)
# 终端 A: redis-cli -a redis123 MONITOR | grep "GET mall:cache:product:" > hotkey_analysis.log
# 运行 30 秒后 Ctrl+C,然后统计:
# cat hotkey_analysis.log | awk '{print $NF}' | sort | uniq -c | sort -rn | head -10
热 key 处理方案:
public class HotKeyHandler {
// 方案 1: 本地缓存前置——热点 key 缓存在应用内存中
private final Cache<String, Object> hotKeyLocalCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterWrite(10, TimeUnit.SECONDS)
.build();
// 方案 2: key 拆分(如库存拆分成多个子库存)
public void distributeStock(String skuId, int totalStock, int shardCount) {
int perShard = totalStock / shardCount;
for (int i = 0; i < shardCount; i++) {
redis.set("mall:stock:" + skuId + ":shard:" + i, String.valueOf(perShard));
}
}
// 方案 3: 读写分离——热 key 的读走从节点
// (需要主从架构,第 23 章展开)
}
3.12 实战十一:配置变更的正确流程
# 查看当前运行时配置
127.0.0.1:6379> CONFIG GET maxmemory
127.0.0.1:6379> CONFIG GET maxmemory-policy
127.0.0.1:6379> CONFIG GET slowlog-log-slower-than
# 临时修改(运行时有效,重启丢失)
127.0.0.1:6379> CONFIG SET maxmemory 1gb
OK
# 永久生效:修改 redis.conf
# 然后重启或执行 CONFIG REWRITE
127.0.0.1:6379> CONFIG REWRITE
OK
# 注: CONFIG REWRITE 会将当前运行时配置写回配置文件
配置变更检查清单:
| 变更项 | 需要确认 | 风险 |
|---|---|---|
| maxmemory 调大 | 服务器内存余量是否足够 | 可能导致 OOM Killer 杀掉 Redis |
| maxmemory-policy 改为 noeviction | 是否所有写入都允许失败 | 写操作全部拒绝 |
| slowlog-log-slower-than 调低 | 日志量会不会过大 | 磁盘写满 |
| timeout 调小 | 是否有长连接的客户端 | 正常客户端被断开 |
| rename-command 禁用 FLUSHALL | 依赖 FLUSHALL 的测试/运维流程 | 运维脚本失效 |
4. 项目总结
4.1 Redis 排障 SOP(标准操作流程)
Step 1: 确认基础连通
PING → INFO server (uptime, version)
Step 2: 检查资源水位
INFO memory (used_memory, maxmemory, evicted_keys)
INFO clients (connected_clients, rejected_connections)
Step 3: 检查性能指标
INFO stats (instantaneous_ops_per_sec, keyspace_hits/misses)
SLOWLOG GET 20
LATENCY DOCTOR
Step 4: 定位问题 key
--bigkeys 扫描
检查无 TTL key (TTL key)
检查 key 类型是否正确 (TYPE key)
Step 5: 分析客户端
CLIENT LIST (异常连接、输出缓冲区)
Step 6: 检查持久化
INFO persistence (RDB/AOF 状态、上次保存时间)
Step 7: 采取行动
必要时: UNLINK 清理大 key, CLIENT KILL 断开异常连接, CONFIG SET 调整参数
最后手段: 重启(需确认持久化正常、有备份)
4.2 常见故障速查表
| 现象 | 优先查看 | 常见原因 |
|---|---|---|
| 接口超时 | SLOWLOG, INFO commandstats | 慢命令阻塞、大 key 删除 |
| 写入失败 | INFO memory | maxmemory 满了且策略是 noeviction |
| 连接超时 | INFO clients | 连接数打满、客户端连接池配置 |
| 缓存命中率骤降 | INFO stats keyspace_misses | 缓存全 miss(过期风暴/FLUSHALL) |
| 内存持续增长 | INFO memory | 无 TTL key 堆积、输出缓冲区增长 |
| 延迟抖动 | LATENCY DOCTOR | fork 子进程、AOF rewrite、过期清理 |
4.3 危险命令清单(生产禁用或受限)
| 命令 | 风险 | 替代方案 |
|---|---|---|
KEYS * |
阻塞主线程,复杂度 O(N) | SCAN |
FLUSHALL/FLUSHDB |
清空所有数据 | 重命名或禁用(ACL) |
CONFIG SET |
运行时修改配置可能引入风险 | 通过配置管理平台 |
MONITOR |
输出所有命令,性能开销 50%+ | 慢查询日志 + 客户端打点 |
SHUTDOWN |
立即关闭 Redis | 通过运维平台执行 |
DEBUG SLEEP |
让 Redis 休眠 | 不要执行 |
大 key DEL |
同步释放大内存,阻塞 | UNLINK 异步删除 |
4.4 思考题
-
排查题:线上 Redis 的
evicted_keys从 0 突然开始增长(每秒约 50 个),但业务方反馈"功能没有异常"。你需要分析这是正常的(缓存淘汰本身就是设计的一部分)还是问题(关键数据被淘汰了)。你会怎么排查? -
设计题:你需要为团队编写一份"Redis 新人值班手册",包含 5 个最常见的告警场景和处理步骤。请选择 5 个场景并写出精简版的处理流程(每个场景不超过 5 步)。
(答案提示将在下一章末尾给出。)
上一章思考题答案提示(第14章):
第1题(积分+排名+流水 Lua):脚本同时操作三个 key——积分
HINCRBY、排名ZINCRBY、流水RPUSH。这三个在同一个 Lua 脚本中是原子的。流水记录格式可以为{userId}:{points}:{timestamp}。第2题(降低 WATCH 冲突率):① 拆 key——将热点账户的余额拆成多个子账户(如
account:1001:shard1),写请求分散到不同 shard;② 排队串行——对同一账户的请求先进入应用层队列,单个线程顺序处理;③ 用 Lua 替代——Lua 在服务端原子执行,不存在客户端 WATCH 重试的开销。
延伸阅读与资源
Redis 8 实战精讲:从 CRUD 到源码,构建高可用缓存系统
Java 工程师进阶:从 JVM 生产排障到OpenJDK原理
MySQL Server 9从入门到进阶的实战之旅
Nacos 3.x 注册配置中心实战修炼:从入门到源码扩展
Elasticsearch从入门到进阶的实战之旅
RabbitMQ从入门到进阶的实战之旅:从单机到大促高可用架构
Celery 入门到进阶之路:从异步任务到自研调度平台
LangGraph 生产级实战进阶:从零到生产级Agent工作流开发
Dify 从入门到源码:LLM 应用平台实战修炼
从零到生产级:FastAPI 异步高并发、源码与 SRE 实战
实战SQLAlchemy 2.0: 从 CRUD 到生产级架构
从零打造企业级 AI 助手:LangChain RAG、Agent 与生产实战
后端工程师 AI 转型课:Ollama 私有化大模型从入门到生产
MongoDB 实战进阶与内核修炼
NumPy 从入门到生产落地:全链路实战指南(科学计算/向量化/性能调优)
Milvus向量数据库实战修炼:从 0 到 1 精通向量检索与生产落地
Python 3实战精进:从脚本到高并发订单引擎
python入门:Rquests从菜鸟脚本到企业级SDK的网络实战圣经

微信公众号: 架构师日常笔记 欢迎关注!
浙公网安备 33010602011771号