缓存淘汰异常应该如何监控和排查?
简化版
缓存淘汰异常不能只看 evicted_keys 总数,要把淘汰速率与内存水位、命中率、回源 QPS、TTL、写入量和分片倾斜关联起来。排查顺序是确认是否触达 maxmemory、识别哪个分片和命名空间增长、判断淘汰策略是否匹配,再区分容量不足、Big Key、TTL 失效或流量/发布变化。
详细版
正常过期由 TTL 触发,内存淘汰则是在达到 maxmemory 后按 policy 选择 Key,两者不能混为一谈。核心指标包括 used_memory/maxmemory、RSS/碎片率、evicted_keys 增量、expired_keys、keyspace hit/miss、命令与网络、各分片 Top Key,以及数据库回源和业务 P99。
发现淘汰突增时先按时间线关联发布、批任务、Key 版本变更和流量;定位是全局还是单分片;按前缀/采样找内存增长来源;检查 TTL 是否缺失或集中;确认 policy 是 allkeys-*、volatile-* 还是 noeviction。不要现场先改策略或 FLUSHDB,否则可能扩大数据库压力并破坏证据。
止血可暂停异常写入、限制低价值缓存、扩容/迁移热点分片、保护数据库回源。根治要补 TTL、修 value/Big Key、调整工作集和策略并做容量预测;验证时看淘汰停止后命中率和数据库压力是否恢复,而非只看内存下降。
完整版教学
一、先区分过期、淘汰与删除
过期是 Key 到达 TTL 后变成不可用,Redis 通过惰性和定期机制清理;淘汰是在 maxmemory 压力下按策略主动移除尚未到期的 Key;业务 DEL/UNLINK 是显式删除。三者原因和风险不同。
如果 expired_keys 上升而 evicted_keys 不变,可能只是大批 TTL 到期;若 evicted_keys 持续上升,说明活跃工作集或写入超过内存预算。把两者都叫“缓存掉了”会误导排障。
记忆钩子:过期是生命周期结束,淘汰是容量被迫做选择;先认清是哪一种,再谈策略。
二、监控要形成因果链而非单指标
仅对累计 evicted_keys 告警没有意义,应取速率并与命中率和回源关联。一次淘汰是否有业务影响,取决于被淘汰的是冷 Key 还是热点、回源是否昂贵,以及是否持续发生。
| 层面 | 指标 | 能回答的问题 |
|---|---|---|
| 内存 | used/max、RSS、fragmentation | 是否真的达到容量上限 |
| 生命周期 | evicted/s、expired/s、TTL 分布 | 是淘汰还是正常过期 |
| 效果 | hit rate、miss QPS | 淘汰是否伤到工作集 |
| 流量 | set bytes/s、网络、命令分布 | 谁在快速写入 |
| 分片 | max/avg memory、Top Key | 是否热点或倾斜 |
| 下游 | DB QPS、连接、P99 | miss 是否正在扩散故障 |
告警最好组合:“淘汰速率上升 + 命中率下降 + DB QPS 上升”比任何单一阈值更可靠。
三、带数字理解淘汰抖动
假设工作集 120GB,集群可安全使用 100GB,每分钟写入 2GB 新数据且旧热数据仍被访问。淘汰只能不断腾出空间,新数据又马上填满,形成 churn。若被淘汰 Key 中 30% 在一分钟内再次访问,系统会反复回源与回填。
入口 5 万 QPS,命中率从 98% 降到 90%,数据库流量从 1000 增至 5000 QPS,增长 5 倍。即使 Redis 延迟仍正常,数据库可能先被拖垮,所以排障不能只盯缓存实例。
reloaded_after_eviction_rate = 被淘汰后短期再次加载的 Key / 淘汰 Key
高比例说明工作集大于容量或策略没有保护热点
四、标准排查时间线
第一步确认告警开始时间和影响业务,冻结非必要变更。第二步定位所有节点还是单分片:单分片优先查 Hot/Big Key 和 slot 倾斜,全局则查容量、TTL 与大规模写入。第三步关联部署、活动、批处理和 Key schema 变化。
第四步采样 Key 前缀与实际内存,检查哪些命名空间增长;第五步查看 TTL 缺失率和分布;第六步确认 maxmemory-policy 与数据语义匹配。最后才决定扩容、迁移、清理或调策略。
淘汰告警 -> 看影响/下游 -> 全局还是单分片
-> 谁在增长 -> TTL/Big Key/发布变化
-> 策略是否匹配 -> 止血 -> 根因修复
保留时间线和样本,避免止血动作清掉所有证据。
五、不同淘汰策略会呈现什么症状
allkeys-lru/lfu 可从所有 Key 选择,适合纯缓存;volatile-* 只淘汰带 TTL 的 Key,无 TTL Key 增长会挤压可淘汰空间;noeviction 达上限后写命令报错,读仍可能成功。策略名称正确不等于结果一定理想,Redis 使用近似采样而非全局精确排序。
LFU 更适合长期热点,LRU 更关注近期访问;但突发扫描可能污染 LRU,错误频率衰减配置也会让 LFU 热度恢复太慢。选择策略前先明确哪些 Key 允许被淘汰,不能让锁、会话或协调状态混在普通缓存池里被随机移除。
策略调整会改变淘汰对象,必须灰度并观察回源,不应在事故中无验证全局切换。
六、如何止血而不制造雪崩
先阻断异常来源:暂停批量回填、限制大 value 写入、关闭低价值新缓存。数据库已有压力时,对 miss 回源限流和请求合并;必要时返回有限 stale 或降级,不能通过全量清缓存“释放内存”。
单分片倾斜可迁移 slot 或拆热点 Key,但迁移占用网络与 CPU,应限速。全局容量不足可快速加节点,扩容前确认重分片时间和副本资源。手工删除应按 owner、TTL 和访问证据选择,并用 UNLINK/分批处理 Big Key。
止血成功的标志是淘汰速率下降、命中稳定、数据库 QPS 回落且内存保有余量,而非某一时刻 used memory 变小。
七、根治与验证
为 Key schema 增加 owner、TTL 默认值、value 大小上限和命名空间预算;对 Top 内存前缀做增长预测;对无 TTL、低命中和 Big Key 建周期报告。容量告警应提前覆盖采购/扩容与迁移时间。
复现真实 TTL、热点和数据量做长稳压测,观察淘汰后短期回填率。故障演练包括单分片失效和副本接管,因为接管节点可能内存/策略不同。所有节点配置要做一致性校验。
evicted_keys_per_sec: 0
expired_keys_per_sec: 820
hit_rate: 98.2%
max_shard_memory_ratio: 72%
keys_without_ttl_ratio: 3.1%
db_fallback_qps: 940
八、常见误区与追问
- 误区:
evicted_keys上升就是 TTL 到期。 TTL 清理记录在过期指标,淘汰是内存压力下移除尚未自然过期的 Key。 - 误区:内存达到上限时直接清库最快。 全量 miss 会把压力转移到数据库,可能造成更大故障。
- 误区:切成 LRU 就能解决容量不足。 工作集持续大于容量时任何策略都会抖动,必须治理数据或扩容。
- 误区:平均内存正常代表集群有余量。 单分片倾斜可能已经持续淘汰,应看最大分片而非平均。
- 追问:为什么
volatile-*可能写失败? 无 TTL Key 不参与淘汰,可淘汰集合不足时无法释放足够空间。 - 追问:如何判断淘汰是否伤害热点? 观察命中率、淘汰后短期重新加载率和数据库回源成本。
- 追问:碎片率高是否一定要重启? 先辨别 RSS/allocator 与数据结构,评估主动碎片整理或滚动迁移,不能直接全停。
九、加强记忆
排查淘汰异常按“分清过期、串起指标、定位分片、找到增长、保护回源、再调策略”推进。evicted_keys 只是起点,要证明它如何影响命中与数据库;事故中先停异常写和限制回源,根治靠 TTL、数据模型、预算与容量预测,而不是把策略名换成 LRU 就结束。