Redis 的过期键删除策略和内存淘汰策略是其管理内存、防止内存耗尽的关键机制,它们解决的是不同层面的问题:
一、过期键删除策略
目标是自动删除那些设置了过期时间且已过期的键,释放其占用的内存。Redis 采用两种策略协同工作:
-
惰性删除
- 原理: 当客户端尝试访问一个键时,Redis 会先检查这个键是否设置了过期时间以及是否已过期。
- 操作: 如果键已过期,Redis 会立即删除这个键,然后才执行客户端的访问命令(返回
/* by yours.tools - online tools website : yours.tools/zh/xpath.html */ nil
或执行失败)。 - 优点: 对 CPU 时间友好。只有在真正访问到过期键时才付出删除的成本。没有访问的过期键即使存在,也不会消耗额外的 CPU 去删除它们。
- 缺点: 对内存不友好。如果一个键设置了过期时间但之后再也没有被访问过,那么它会一直占用内存空间,直到被访问或者被定期删除策略扫描到并删除。这相当于一种“内存泄漏”。
-
定期删除
- 原理: Redis 会周期性地、主动地从设置了过期时间的键集合中随机抽取一部分键(默认每次检查 20 个键),检查它们是否过期。
- 操作:
- 每次执行时,从过期字典中随机选择一定数量的键。
- 删除其中所有已过期的键。
- 如果发现本次检查中过期的键比例超过一定阈值(默认是 25%),则立即再随机抽取一批键进行检查(循环),直到过期键比例降到阈值以下或达到时间限制(避免过度占用 CPU)。
- 优点: 一定程度上减少了惰性删除带来的内存浪费问题,通过周期性扫描清理掉那些长期不被访问的过期键。
- 缺点: 需要平衡扫描的频率、每次扫描的数量以及 CPU 消耗。扫描太频繁或每次扫描太多键会消耗过多 CPU;扫描太少或太慢则可能导致大量过期键堆积。它是一个折中方案,无法保证实时删除所有过期键。
总结: Redis 通过惰性删除 + 定期删除的组合策略来管理过期键。惰性删除确保访问时数据是最新的且能释放内存;定期删除则像一个“清洁工”,定期清理那些“僵尸”过期键,减少内存占用。两者互补,但都无法保证绝对实时删除所有过期键。
二、内存淘汰策略
目标是当 Redis 使用的内存达到
/* by yours.tools - online tools website : yours.tools/zh/xpath.html */ maxmemory
配置的限制时,Redis 如何选择要删除哪些键(无论是否过期)来释放空间,以便写入新数据。这是防止 Redis 因内存耗尽而崩溃的最后一道防线。
Redis 提供了 8 种内存淘汰策略,通过 maxmemory-policy
配置项指定:
noeviction
:默认策略。当内存不足时,新写入操作会报错(如(error) OOM command not allowed when used memory > 'maxmemory'
)。读操作通常不受影响。适用于你确信数据绝对不能丢失且宁愿拒绝写入也不能覆盖数据的场景。allkeys-lru
:从所有键空间中,使用 LRU 算法淘汰最近最少使用的键。这是最常用且通常效果较好的策略。volatile-lru
:从设置了过期时间的键空间中,使用 LRU 算法淘汰最近最少使用的键。allkeys-lfu
:从所有键空间中,使用 LFU 算法淘汰最不经常使用的键(根据访问频率)。适用于访问模式随时间变化较大的场景。volatile-lfu
:从设置了过期时间的键空间中,使用 LFU 算法淘汰最不经常使用的键。allkeys-random
:从所有键空间中,随机淘汰任意键。volatile-random
:从设置了过期时间的键空间中,随机淘汰任意键。volatile-ttl
:从设置了过期时间的键空间中,淘汰剩余生存时间最短的键。即优先淘汰即将过期的键。
关键点说明
- 作用时机: 仅当内存使用达到
maxmemory
限制且客户端尝试执行会增加内存使用量的命令(如SET
,LPUSH
,HINCRBY
等)时触发淘汰。 - 作用范围: 以
allkeys-
开头的策略会淘汰所有键(包括未设置过期时间的键)。以volatile-
开头的策略只淘汰设置了过期时间的键。 volatile-lru
/volatile-lfu
/volatile-random
/volatile-ttl
的特殊情况: 如果内存不足时,淘汰池(设置了过期时间的键)中没有合适的键可淘汰(例如没有键设置过期时间),那么这些策略的行为会退化成noeviction
,新写入操作会报错。- LRU vs LFU:
- LRU (Least Recently Used): 关注“最近使用时间”。认为最近没被用过的键将来也不太可能被用到。实现上是近似 LRU(采样)。
- LFU (Least Frequently Used): 关注“访问频率”。认为访问次数最少的键将来也不太可能被用到。Redis 实现的 LFU 会随时间衰减访问计数(避免旧热点数据长期霸占)。
- 选择策略:
- 如果数据访问模式接近 LRU,或者不确定,
allkeys-lru
通常是最佳选择。 - 如果数据有明显的热点和冷门区分,且热点访问频繁,
allkeys-lfu
可能更好。 - 如果你希望 Redis 只作为缓存使用(数据可丢失),所有键都设置了过期时间,并且希望优先淘汰快过期的数据,可以用
volatile-ttl
。 - 如果你有部分持久化数据(不能淘汰)和部分缓存数据(可淘汰),确保缓存数据都设置了过期时间,然后使用
volatile-lru
或volatile-lfu
。 noeviction
适用于数据绝对不能丢失的场景(但要做好容量规划和监控)。allkeys-random
和volatile-random
通常效果不如基于 LRU/LFU 的策略,除非你的访问模式是完全随机的。
- 如果数据访问模式接近 LRU,或者不确定,
总结: 内存淘汰策略是 Redis 在内存耗尽时的应急机制,用于释放空间以允许新数据写入。你需要根据数据的特性(是否可丢失、访问模式、是否都设过期时间)来谨慎选择合适的策略。allkeys-lru
或 allkeys-lfu
是最常用和推荐的通用策略。务必配置 maxmemory
并根据需要设置 maxmemory-policy
。