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_policy、maxmemory、used_memory、used_memory_dataset 和 evicted_keys。如果 maxmemory 是 0,表示没有为数据集设置上限,此时不会因为该阈值启动淘汰。若策略实际是 noeviction,现象也不是“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
如果实例里大量键都返回 -1,volatile-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 选项,并在上线前确认该选项的版本和语义;不要凭命令名称猜测。

用 INFO 区分未触发、无候选和淘汰异常
| 观察项 | 说明 | 下一步 |
|---|---|---|
used_memory 未接近 maxmemory | 还没到触发条件 | 先检查上限、复制/AOF缓冲和增长来源 |
| 达到上限,TTL 多为 -1 | volatile 集合为空或太小 | 修复写入 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_keys、evicted_keys 和应用日志判断。
调大 maxmemory-samples 能解决没有候选键吗?
不能。它只影响 LRU 候选采样近似程度,不能把永久键加入 volatile-lru 的候选集合。
-
117 收藏
-
426 收藏
-
171 收藏
-
113 收藏
-
195 收藏
-
325 收藏
-
460 收藏
-
480 收藏
-
152 收藏
-
数据库 · Redis | 8小时前 | Redis · cluster · slot迁移 · MOVED · ASK · 客户端路由 · redis slot Redis Cluster MOVED ASK reshard 拓扑刷新157 收藏
-
188 收藏
-
470 收藏
-
251 收藏
-
326 收藏
-
125 收藏
-
267 收藏
-
数据库 · Redis | 23小时前 | Redis · 故障恢复 · Streams · 消费者组 · 消息重试 · redis 消费者组 XPENDING XAUTOCLAIM Redis Streams380 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习