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

Redis volatile-lru 没有淘汰键时先检查什么

来源:17golang原创

时间:2026-09-07 23:01:49 279浏览 收藏

Redis 配了 maxmemory-policy volatile-lru,内存到上限后却没有淘汰键,最先要查的不是把 maxmemory-samples 调大,而是候选键有没有过期时间。volatile-lru 只在“带 TTL 的键”里挑最近最少使用者;如果整个实例没有可过期键,它的表现就接近 noeviction,新写入可能收到内存限制错误。

排查顺序应是:确认实际策略和上限 → 用 TTL 看键是否真的带过期时间 → 检查覆盖写是否抹掉 TTL → 再用 INFO 判断是否触发过淘汰。
要点速览
  • volatile-lru 的候选集合不是所有键,而是设置了过期时间的键。
  • TTL 返回 -1 表示键永久存在,返回 -2 表示键已经不存在。
  • 纯缓存实例通常更适合 allkeys-lru;混合存储则要先修正 TTL 生命周期。

先确认 Redis 真的在用 volatile-lru

先读运行时配置,不要只看磁盘里的 redis.conf。容器启动参数、托管服务默认值或 CONFIG SET 都可能让实际值不同。

# 读取运行中的策略和最大数据集内存
redis-cli CONFIG GET maxmemory-policy
redis-cli CONFIG GET maxmemory

# 查看当前内存和统计信息
redis-cli INFO memory
redis-cli INFO stats

重点记录 maxmemory_policymaxmemoryused_memoryused_memory_datasetevicted_keys。如果 maxmemory0,表示没有为数据集设置上限,此时不会因为该阈值启动淘汰。若策略实际是 noeviction,现象也不是“volatile-lru 没找到键”。

Redis volatile-lru 中 maxmemory、淘汰策略与带 TTL 键候选集合的静态关系图
图1:从实例内存边界、淘汰策略到带 TTL 候选集合,理解 volatile-lru 为什么不会选择永久键。

TTL 返回值能直接判断有没有淘汰候选

对正在写入的缓存键执行 TTL,它比“我在代码里设置过过期时间”更可靠,因为它反映的是 Redis 当前保存的状态。

# -1:键存在但没有过期时间;-2:键不存在
redis-cli SET cache:user:42 '{"name":"Ada"}'
redis-cli TTL cache:user:42

# 用 EX 让新写入同时建立 TTL
redis-cli SET cache:user:42 '{"name":"Ada"}' EX 300
redis-cli TTL cache:user:42

# 也可以分两次写,但要留意中间窗口
redis-cli SET cache:user:42 '{"name":"Ada"}'
redis-cli EXPIRE cache:user:42 300

如果实例里大量键都返回 -1volatile-lru 没有候选键是正常结果。TTL 的返回值是秒级整数,剩余时间减少到零后键会被视为过期;不要把一次读取到的 TTL 当成精确的淘汰时间。

覆盖写为什么会让原来的 TTL 消失

最容易漏掉的是缓存续写逻辑:第一次用 SET ... EX 300 建立键,后续为了更新内容却执行了不带过期参数的 SET。这个覆盖写会把键变成永久键,随后它不再属于 volatile-lru 候选集合。

# 第一次写入带 TTL
redis-cli SET cache:profile:42 v1 EX 300

# 错误示例:覆盖值时没有保留过期时间
redis-cli SET cache:profile:42 v2
redis-cli TTL cache:profile:42  # 结果通常为 -1

# 修复方式:更新时再次明确设置 TTL
redis-cli SET cache:profile:42 v3 EX 300

# 续期语义要单独决定,不要无意中永久续命
redis-cli EXPIRE cache:profile:42 300

在客户端封装层,把“写值”和“过期策略”放在同一个缓存方法里更稳妥。若业务要求只在首次写入时设置过期时间,使用客户端支持的保留 TTL 选项,并在上线前确认该选项的版本和语义;不要凭命令名称猜测。

Redis 缓存键、TTL 元数据与无 TTL 覆盖写导致候选资格变化的静态关系图
图2:把缓存值和 TTL 元数据分开看,定位覆盖写后键从 volatile-lru 候选集合中消失的原因。

用 INFO 区分未触发、无候选和淘汰异常

观察项说明下一步
used_memory 未接近 maxmemory还没到触发条件先检查上限、复制/AOF缓冲和增长来源
达到上限,TTL 多为 -1volatile 集合为空或太小修复写入 TTL,或评估 allkeys-lru
evicted_keys 增长已经发生过淘汰观察命中率与被淘汰数据是否符合预期
新写入报内存限制错误没有足够候选键,或实际策略为 noeviction核对运行时策略后再改配置

Redis 官方文档还提示,复制或 AOF 场景存在不计入淘汰比较的缓冲内存,因此不能只拿机器总内存和 maxmemory 做简单相减。生产上应保留缓冲空间,并持续观察 mem_not_counted_for_evict 等内存字段。

按数据边界选择修复方式

如果实例同时放缓存和必须保留的业务键,volatile-lru 的价值在于只淘汰有 TTL 的键,但前提是缓存键始终正确设置 TTL。若这是纯缓存实例,永久键本身通常就是写入缺陷,此时可以统一补上过期时间,或评估 allkeys-lru 让所有键都进入 LRU 候选范围。若永久数据与缓存数据都很重要,拆分实例比依赖一条淘汰策略更容易解释和运维。

改策略前先做一轮清单:哪些键允许丢失、哪些键必须保留、写入是否总带 TTL、超限时调用方能否接受错误,以及回源是否有保护。不要因为“没有淘汰键”就直接把所有键改成可淘汰。

常见问题

volatile-lru 会主动给没有 TTL 的键补过期时间吗?

不会。它只从已有过期时间的键中选择候选,不负责改变键的生命周期。

TTL 返回 -2 说明淘汰了吗?

不一定。键可能自然过期、被 DEL 删除,或被淘汰;需要结合 expired_keysevicted_keys 和应用日志判断。

调大 maxmemory-samples 能解决没有候选键吗?

不能。它只影响 LRU 候选采样近似程度,不能把永久键加入 volatile-lru 的候选集合。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>