应重点关注commandstats中evict的calls、usec和usec_per_call三项指标,因为evicted_keys仅反映淘汰结果,无法体现CPU开销;真正消耗CPU的是采样、空闲计算等淘汰过程逻辑,其耗时统一计入evict分类。

直接看 commandstats 里 evict 相关指标,如果 evict 耗时占所有命令总耗时 >5%,基本说明淘汰策略正在拖慢 Redis 主线程
为什么不能只看 evicted_keys 数量?
因为淘汰动作本身不记录在命令统计里,evicted_keys 只是结果计数,无法反映 CPU 开销。真正吃 CPU 的是淘汰过程中的采样、空闲时间计算、候选池维护等逻辑——这些都归在 commandstats 的 evict 分类下。
常见误判场景:
- 看到
evicted_keys每秒几百个,就以为“淘汰很频繁”,但实际evict耗时可能只有 0.2% —— 说明采样少、key 访问模式集中,淘汰开销极低 - 相反,
evicted_keys很低(比如每秒几个),但evict耗时占比突然飙到 8%,大概率是maxmemory-samples被调得太高,或用了allkeys-lfu+ 高频写入导致 LFU 计数器频繁更新
INFO commandstats 中必须盯住的三项
执行 INFO commandstats 后,定位到类似这一行:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
cmdstat_evict:calls=12482,usec=8942312,usec_per_call=716.43
重点关注:
-
calls:不是“淘汰了多少 key”,而是“触发了多少次淘汰流程”。一次淘汰流程可能删 1 个 key,也可能删 N 个(取决于maxmemory-policy和当前内存压力) -
usec:所有淘汰流程累计消耗的微秒数,是判断 CPU 压力的核心依据 -
usec_per_call:单次淘汰流程平均耗时。若 >1000 μs(即 1ms),说明采样或 LFU 计数逻辑已成瓶颈,尤其当maxmemory-samples≥ 10 且数据集 >100 万 key 时更明显
哪些淘汰策略更容易拉高 evict 耗时?
不是所有策略对 CPU 影响一样。真实压测和线上观察表明:
-
allkeys-lfu在写密集场景下最危险:每次写操作都要更新 LFU 计数器,usec_per_call容易突破 2000 μs,尤其配合lfu-log-factor>10 时 -
volatile-lru和allkeys-lru耗时接近,但前者在过期 key 稀疏时会反复采样无效 key,导致calls异常升高 -
allkeys-random几乎不增加evict开销,usec_per_call通常 - 无论哪种策略,只要
maxmemory-samples从默认 5 改成 20,usec_per_call普遍翻倍——这不是线性增长,而是采样后排序/比较的复杂度上升
怎么快速验证是不是淘汰策略的问题?
别猜,用对比法:
- 先记下当前
INFO commandstats里cmdstat_evict:usec和总usec(所有命令之和),算出占比 - 临时切到
noeviction策略:CONFIG SET maxmemory-policy noeviction(注意:这会让写失败,仅用于诊断) - 等 30 秒,再跑一次
INFO commandstats,观察其他命令(如cmdstat_set、cmdstat_get)的usec_per_call是否明显下降(降幅 >15% 就是淘汰在抢 CPU) - 恢复原策略,再针对性调低
maxmemory-samples或换用allkeys-lru替代allkeys-lfu
真正容易被忽略的是:淘汰耗时不是孤立存在的。它和客户端请求节奏强耦合——当大量请求集中在某几秒涌入,而内存又刚好触达 maxmemory,就会引发淘汰风暴,此时 evict 的 calls 可能在一秒内暴涨十倍,但 usec_per_call 反而下降(因为采样逻辑被批量复用)。这种脉冲式压力,光看平均值会漏掉关键毛刺。

















