3. Redis 高级特性:过期策略、内存淘汰机制与持久化对故障排查的影响
在缓存穿透与 gRPC 接口故障的实战排查中,Redis 的过期策略、内存淘汰机制和持久化配置往往是“隐形杀手”。这些高级特性如果配置不当,会导致缓存雪崩、数据不一致、接口超时等严重问题。本章将深入剖析这些特性对故障排查的具体影响,并提供可操作的诊断方法。
3.1 过期策略:缓存雪崩与穿透的导火索
Redis 的过期策略决定了 key 何时被删除。在故障排查中,最常见的两个场景是“大量 key 同时过期”和“过期 key 未被及时清理”。
3.1.1 过期删除机制对比
| 机制 | 工作原理 | 对故障的影响 | 排查要点 |
|---|---|---|---|
| 惰性删除 | 访问 key 时检查是否过期,过期则删除 | 过期 key 可能长期占用内存,导致内存碎片和淘汰压力 | 检查 INFO keyspace 中过期 key 数量与 expired_keys 指标 |
| 定期删除 | 每 100ms 随机抽样检查,删除过期 key | 抽样频率不足时,大量过期 key 堆积,触发内存淘汰 | 监控 hz 参数(默认 10),调整至 100 可提高清理频率 |
| 主动过期通知 | Redis 6.0+ 支持 keyspace-events 通知 |
可监听过期事件,但可能增加 CPU 开销 | 使用 SUBSCRIBE __keyevent@0__:expired 验证通知是否触发 |
实战陷阱: 当业务代码为大量缓存 key 设置了相同的 TTL(如 3600 秒),这些 key 会在同一秒内过期。此时如果大量请求同时穿透到数据库,就会引发缓存雪崩。排查时请检查
TTL 分布是否均匀。
3.1.2 过期策略与缓存穿透的关联
缓存穿透的本质是“请求绕过了缓存直接打到数据库”。过期策略在其中扮演的角色是:
- 过期 key 未被及时删除:惰性删除导致过期 key 仍占用内存,新写入的缓存 key 可能因内存不足被淘汰,形成“缓存空洞”。
- 定期删除频率过低:默认
hz=10时,每秒只执行 10 次定期删除,大量过期 key 堆积会触发内存淘汰,间接导致缓存穿透。 - TTL 设置不合理:热点数据 TTL 过短,导致频繁重建缓存,增加数据库压力。
排查命令: 使用
redis-cli --bigkeys 扫描大 key,结合 OBJECT idletime 判断 key 是否长期未被访问(惰性删除未触发)。使用 INFO stats 查看 expired_keys 和 evicted_keys 的实时变化。
3.2 内存淘汰机制:LRU/LFU/TTL 的故障排查影响
当 Redis 内存使用达到 maxmemory 上限时,淘汰机制开始工作。不同的淘汰策略对故障排查有截然不同的影响。
3.2.1 淘汰策略对比与选择
| 策略 | 淘汰依据 | 适用场景 | 故障风险 |
|---|---|---|---|
| allkeys-lru | 最近最少使用(全局) | 通用缓存,热点数据访问模式 | 低,但可能淘汰未设置 TTL 的 key |
| allkeys-lfu | 最不经常使用(全局) | 访问频率差异大的场景 | 低,但新 key 容易被误淘汰 |
| volatile-ttl | 剩余 TTL 最短 | 需要精确控制过期时间的场景 | 高,TTL 设置不当会导致大量 key 同时被淘汰 |
| volatile-lru | 最近最少使用(仅限过期 key) | 混合使用持久化和过期 key | 中,持久化 key 可能占用大量内存 |
| noeviction | 不淘汰,写入返回 OOM 错误 | 严格保证数据不丢失 | 极高,写入失败直接导致业务异常 |
故障排查案例: 某 gRPC 接口在高峰期出现大量超时,排查发现 Redis 配置为
volatile-ttl,且业务代码为所有缓存 key 设置了相同的 TTL。当 TTL 到期时,大量 key 被同时淘汰,后续请求全部穿透到数据库,导致数据库连接池耗尽。解决方案:改用 allkeys-lru 并给 TTL 增加随机偏移。
3.2.2 淘汰机制与缓存击穿的关联
缓存击穿(热点 key 失效)与淘汰机制密切相关:
- LRU 策略下的热点 key 被淘汰:当内存不足时,LRU 可能淘汰访问频率高但最近未被访问的热点 key,导致击穿。
- LFU 策略的“冷启动”问题:新写入的 key 访问频率初始为 0,容易被淘汰,导致缓存命中率下降。
- TTL 策略的“集体过期”:多个 key 剩余 TTL 相同时,会被同时淘汰,形成“淘汰风暴”。
3.3 持久化机制:RDB 与 AOF 对故障恢复的影响
持久化机制决定了 Redis 重启后缓存数据的恢复情况。在故障排查中,持久化配置不当会导致“缓存预热失败”或“数据不一致”。
3.3.1 RDB 与 AOF 对比
| 特性 | RDB(快照) | AOF(追加文件) |
|---|---|---|
| 数据完整性 | 可能丢失最后一次快照后的数据 | 根据 fsync 策略,最多丢失 1 秒数据 |
| 恢复速度 | 快,直接加载快照文件 | 慢,需要重放所有写命令 |
| 对性能的影响 | fork 子进程时可能阻塞主进程 | 写操作频繁时增加磁盘 I/O |
| 故障排查价值 | 可快速恢复缓存,但可能丢失过期 key 的删除记录 | 可精确回放操作,但恢复期间缓存不可用 |
3.3.2 持久化对缓存穿透故障的影响
在缓存穿透故障中,持久化机制的影响主要体现在以下场景:
- Redis 重启后缓存为空:如果未开启持久化,重启后所有缓存丢失,所有请求直接穿透到数据库,形成“缓存雪崩”。
- AOF 重写导致 CPU 飙升:AOF 重写时 fork 子进程,如果内存较大(>10GB),可能导致主进程阻塞,影响缓存读写性能。
- RDB 快照与过期 key:RDB 快照保存的是生成时刻的数据,如果快照生成后大量 key 过期,重启后这些 key 仍然存在,导致缓存与数据库不一致。
关键排查点: 在 Redis 重启后,使用
INFO keyspace 检查 key 数量是否与重启前一致。如果 key 数量明显减少,说明持久化未保存所有数据;如果 key 数量异常增多,说明过期 key 未被正确删除。
3.3.3 持久化配置建议
# 推荐配置(平衡数据安全与性能)
save 900 1 # 900 秒内至少 1 次修改,触发 RDB
save 300 10 # 300 秒内至少 10 次修改,触发 RDB
save 60 10000 # 60 秒内至少 10000 次修改,触发 RDB
appendonly yes
appendfsync everysec # 每秒 fsync,最多丢失 1 秒数据
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 故障排查时检查
# 1. RDB 文件大小:ls -lh /var/lib/redis/dump.rdb
# 2. AOF 文件大小:ls -lh /var/lib/redis/appendonly.aof
# 3. 重启后 key 数量:redis-cli INFO keyspace | grep keys
3.4 综合故障排查清单
当 gRPC 接口出现超时或数据不一致时,请按以下清单排查 Redis 高级特性相关的问题:
- 检查过期策略:使用
INFO stats查看expired_keys和evicted_keys,判断是否存在大量 key 同时过期或淘汰。 - 检查淘汰策略:使用
CONFIG GET maxmemory-policy确认当前策略,结合MEMORY USAGE命令分析热点 key 是否被淘汰。 - 检查持久化状态:使用
INFO persistence查看 RDB 和 AOF 的状态,确认最后一次持久化时间是否在故障发生前。 - 检查 TTL 分布:使用
redis-cli --scan --pattern "*" | xargs -I {} redis-cli TTL {}分析 TTL 分布,查找是否存在大量相同 TTL 的 key。 - 检查内存碎片:使用
INFO memory查看mem_fragmentation_ratio,如果大于 1.5,说明内存碎片严重,可能影响淘汰效率。
自动化排查脚本: 将上述检查点封装为
redis_fault_check.sh,在故障发生时自动采集所有指标,便于事后分析。