登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  Redis

Redis LFU 淘汰中的计数衰减参数怎样理解

来源:17golang原创

时间:2026-10-09 08:53:51 461浏览 收藏

lfu-decay-time 表示 Redis LFU 频率计数减少 1 个单位所对应的时间窗口,单位是分钟。默认值为 1,意味着每经过 1 分钟,计数在被重新计算时最多按经过的完整周期递减;设为 0 则不做衰减。它不是键的 TTL,也不会每隔一段时间把所有键的计数统一清零。

官方淘汰策略文档:https://redis.io/docs/latest/develop/reference/eviction/

要点速览
  • 数值越小,旧热点降温越快;数值越大,历史热度保留越久。
  • 衰减是按已过去的完整周期扣减计数,不是乘一个百分比。
  • lfu-log-factor 控制计数增长概率,lfu-decay-time 控制热度消退速度。

这个参数控制的到底是什么

Redis 的 LFU 不是为每个键保存一个无限增长的精确访问次数,而是在键的元数据中保存一个 8 位对数频率计数器。计数器上限为 255,访问时采用概率方式增长;为了避免几小时前或几天前的热点永久占据优势,Redis 又引入时间衰减。

可以把衰减理解成下面的关系:经过的完整衰减周期数约等于“距离上次记录的分钟数 ÷ lfu-decay-time”,当前计数再减去这些周期数,最低降到 0。例如参数为 5 时,完整经过 20 分钟对应 4 个衰减周期,而不是在第 5 分钟直接归零。

配置值含义典型影响
0计数不随时间衰减历史热点可能长期占优,除非访问模式非常稳定,否则要谨慎
1每 1 分钟形成一个衰减周期默认值,热点能较快随访问变化调整
5每 5 分钟形成一个衰减周期短暂停顿不易让长期热点快速失去优势
60每 60 分钟形成一个衰减周期历史热度记忆很长,突发热点退出也更慢

LFU 元数据怎样保存时间和频率

在 LFU 策略下,Redis 使用 24 位元数据同时表达时间与频率:高 16 位保存分钟级的最近衰减时间,低 8 位保存对数频率计数。lfu-decay-time 参与“经过多少衰减周期”的计算,OBJECT FREQ 则让管理员读取键当前的对数频率计数。

Redis LFU 键元数据、衰减周期和 OBJECT FREQ 之间的静态关系图
图1:Redis LFU 元数据结构说明图;展示时间字段、频率字段与衰减参数的关系,不是运行截图。

一个容易误解的细节是:Redis 不会由后台任务每分钟遍历所有键并修改计数。衰减是惰性计算的,键在被访问、更新、作为淘汰候选检查,或者通过相关命令读取频率时,才根据时间差计算应扣减多少。这种设计避免了为整个键空间持续扫描,但也意味着“配置改完以后,所有键的数值马上同时变化”并不是正确预期。

用 OBJECT FREQ 观察衰减效果

OBJECT FREQ key 返回的是对数频率计数,不是精确请求总数,而且只有在 maxmemory-policy 选择 LFU 策略时才可使用。测试时先确认策略和参数,再建立访问量不同的键进行观察。

# 查看当前淘汰策略、增长因子和衰减时间,避免在错误策略下解释频率值
redis-cli CONFIG GET maxmemory-policy
redis-cli CONFIG GET lfu-log-factor
redis-cli CONFIG GET lfu-decay-time

# 仅在隔离测试实例中切换 LFU 策略,不要直接改动未知生产环境
redis-cli CONFIG SET maxmemory-policy allkeys-lfu

# 建立两个相同类型的测试键,后续给它们不同访问量
redis-cli SET demo:hot value
redis-cli SET demo:cold value

# 给热键增加访问,概率计数不会与循环次数一一对应
for i in {1..200}; do redis-cli GET demo:hot >/dev/null; done

# 读取当前对数频率计数,用于同一环境中的相对比较
redis-cli OBJECT FREQ demo:hot
redis-cli OBJECT FREQ demo:cold

等待若干完整衰减周期后再次读取,可以看到计数逐步降低。不要拿 OBJECT FREQ 当 QPS 指标,也不要期待 200 次 GET 就一定增加 200;计数增长本来就是概率性的。验证重点是相同配置下热键与冷键的相对差异,以及改变衰减时间后旧热点退出的速度是否符合业务周期。

要临时调整测试实例,可以这样做:

# 把衰减周期改为 5 分钟,观察旧热点是否保留得更久
redis-cli CONFIG SET lfu-decay-time 5

# 读取生效值;如需重启后仍保留,还要按部署方式更新配置来源
redis-cli CONFIG GET lfu-decay-time

# 查看淘汰累计数,结合命中率与业务延迟判断变化,不只看单个键
redis-cli INFO stats | grep -E 'evicted_keys|keyspace_hits|keyspace_misses'

别把三个 LFU 相关参数混在一起

LFU 的实际淘汰结果不是只由一个参数决定。增长、衰减和候选采样属于三个不同边界:lfu-log-factor 决定高频键继续增长有多难,lfu-decay-time 决定历史热度消退有多快,maxmemory-samples 决定每次淘汰近似算法观察多少候选键。

Redis LFU 增长参数、衰减参数与候选采样参数的静态边界关系图
图2:Redis LFU 调节参数边界说明图;三个参数共同影响淘汰判断,但职责不同。
参数主要职责调大后的方向常见误区
lfu-log-factor控制访问计数的概率增长高频计数增长更慢,需要更多命中才能上升把它当成衰减速度
lfu-decay-time控制计数每减少 1 的时间窗口旧热度保留更久把它当成 TTL 或清零周期
maxmemory-samples控制近似淘汰每次比较的候选数量更接近理想选择,但消耗更多 CPU认为它会改变单个键的频率计数

此外,allkeys-lfu 可以从全部键中按 LFU 近似淘汰,而 volatile-lfu 只在设置了过期时间的键中选择候选。如果使用后者但大量键没有 TTL,没有资格进入候选集合的键就不会因为频率低而被淘汰。排查效果时要先确认策略边界,再看计数参数。

不同访问模式怎样选择衰减时间

默认值通常应作为起点,只有当业务的热点周期和默认衰减明显不匹配时再调整。可以先按访问模式判断方向:

  • 热点以分钟为单位快速切换:保持较小值,让旧热点尽快降温,避免活动页、榜单或突发事件结束后仍占据缓存。
  • 热点跨小时稳定:可以评估适度增大,减少短时抖动对长期热点的影响。
  • 访问非常稀疏:过大的值会让偶然被访问过的键长时间保留优势,应结合内存压力和 miss 成本判断。
  • 冷热差距极大:先观察 OBJECT FREQ 的分布,再决定是改衰减时间还是增长因子;只动一个参数更容易解释结果。

不要根据“多久没有访问就淘汰”来反推该参数,因为 LFU 不是精确的空闲时间淘汰器。计数衰减后,Redis 仍要在内存达到 maxmemory、写入需要释放空间时,从采样候选中选择更不常用的键。没有内存压力时,低频键不会仅因计数下降而自动删除。

上线调整时要观察哪些风险

调整应先在与生产流量形态相近的测试实例或小流量实例进行,并保留变更前的策略、参数和监控基线。至少同时观察命中率、淘汰数、内存使用、写入延迟和关键业务回源压力,不能只抽查几个键的 OBJECT FREQ。

  1. 记录现有 maxmemory-policy、lfu-log-factor、lfu-decay-time 和 maxmemory-samples。
  2. 一次只调整一个变量,并让观察窗口覆盖多个真实热点周期。
  3. 比较 keyspace_hits、keyspace_misses、evicted_keys 与上游回源延迟。
  4. 确认配置的持久化方式;仅执行 CONFIG SET 可能不会自动修改容器、配置文件或配置中心。
  5. 准备回退值,若 miss 增长或旧热点保护过强,就恢复原参数而不是继续叠加调整。

常见问题

lfu-decay-time=1 是每分钟清零吗?

不是。它表示每经过一个完整分钟周期,当前计数可减少 1,直到最低为 0;不会每分钟把所有键统一清零。

设为 0 会让键永远不被淘汰吗?

不会。0 只表示频率计数不因时间衰减。键仍可能因为相对频率较低、候选采样结果以及内存压力而被淘汰,但历史热点更容易长期保留较高计数。

改完参数后为什么 OBJECT FREQ 没有立刻整体下降?

因为衰减是惰性计算,不是后台统一扫描。键被访问、更新、读取频率或进入淘汰候选检查时,才会根据经过时间计算新的计数。

可以只看 OBJECT FREQ 决定参数吗?

不够。它适合解释单个键的相对热度,还要同时看命中率、淘汰数、内存压力、延迟和上游回源成本,才能判断参数是否真正改善了缓存效果。

LFU 和 TTL 会互相替代吗?

不会。TTL 决定键何时过期,LFU 决定内存压力下哪些候选更应该被淘汰。二者可以同时使用,但语义和触发条件不同。

参考资料

  • Redis Key eviction:https://redis.io/docs/latest/develop/reference/eviction/
  • Redis 官方配置示例:https://github.com/redis/redis/blob/unstable/redis.conf
  • OBJECT FREQ:https://redis.io/docs/latest/commands/object-freq/
  • Redis 淘汰实现源码:https://github.com/redis/redis/blob/unstable/src/evict.c
声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>