Redis 内存淘汰策略

Redis 把数据放在内存里,内存总有上限。当写入使内存超过配置的 maxmemory 上限时,Redis 必须决定"腾出空间给新数据",这就是内存淘汰(Eviction)策略。做缓存时选错策略会导致命中率低下甚至写失败,是上线前必须想清楚的配置。

配置内存上限

maxmemory 为 0 表示不限制(默认),设置了上限后内存触顶才触发淘汰。可在 redis.conf 配置,也可运行时用 CONFIG SET 动态调整、无需重启:

# redis.conf 中配置
maxmemory 100mb
maxmemory-policy allkeys-lru
maxmemory-samples 5     # 淘汰采样数,默认 5,见下文
CONFIG SET maxmemory 100mb
# 输出:OK
CONFIG SET maxmemory-policy allkeys-lru
# 输出:OK
CONFIG GET maxmemory
# 输出:1) "maxmemory"
#       2) "104857600"

八种策略

策略淘汰范围语义
noeviction不淘汰内存满后写命令直接报错(默认策略)
allkeys-lru所有键淘汰最近最少使用的键
allkeys-lfu所有键淘汰使用频率最低的键(需 4.0+)
allkeys-random所有键随机淘汰
volatile-lru设了 TTL 的键只在会过期的键里按 LRU 淘汰
volatile-lfu设了 TTL 的键只在会过期的键里按 LFU 淘汰
volatile-random设了 TTL 的键只在会过期的键里随机淘汰
volatile-ttl设了 TTL 的键优先淘汰剩余存活时间最短的键

volatile-* 系列在"没有任何设了 TTL 的键"时退化为 noeviction 行为(不淘汰、写报错)。

LRU 与 LFU 的近似实现

真正的 LRU 需记录每个键的最后访问时间,内存代价大。Redis 采用采样近似:从键空间随机抽 maxmemory-samples(默认 5)个键,淘汰其中"最久未用"的一个,效果接近真实 LRU。LFU 则为每个键维护一个随时间衰减的访问计数器(可用 lfu-log-factor、lfu-decay-time 调节增长与衰减),能识别"访问次数少但最近被碰过"的冷门键,适合热点集中的场景。

淘汰与过期的区别

过期(Expire)是"键自带 TTL、到点删除",属于数据自身生命周期;淘汰是"内存触顶、按策略挑键删除",属于资源不够时的被动腾挪。两者相互独立:不过期的键可能被 allkeys-* 淘汰;设了 TTL 的键即使没到点,也可能被策略提前淘汰。

noeviction 下的表现

把策略设为 noeviction 且内存写满时,写命令会直接报 OOM 错误,读命令不受影响:

CONFIG SET maxmemory 5mb
# 输出:OK
SET bigkey "aaaaaaaaaaaaaaaaaaaaaaaa…(写入超过 5MB 的字符串)"
# 输出:(error) OOM command not allowed when used memory > 'maxmemory'.

被淘汰的键数量可在 INFO stats 的 evicted_keys 字段观察:

INFO stats
# 输出:… evicted_keys:0 …(每淘汰一个键该计数 +1)

实战选型建议

  • 纯缓存、数据可重建:allkeys-lru 最省心;热点极集中(如热门榜单)可换 allkeys-lfu。
  • 只有一部分键允许淘汰(设了 TTL),其余必须常驻:选 volatile-lru。
  • 数据不可丢(计数、会话强一致):绝不能开淘汰,保持 noeviction 并做好容量监控与告警。
  • volatile-random、volatile-ttl 少见;volatile-ttl 会优先删"本来就要过期"的键,缓存意义不大,一般不用。

小结

内存淘汰 = maxmemory 上限 + 策略选择:缓存场景首选 allkeys-lru / allkeys-lfu,不可丢数据用 noeviction 并监控容量。它和"过期"是两回事——过期是键的寿命到点,淘汰是内存不够时的被动删除。淘汰发生在写路径上、一旦触发有性能开销,容量规划比策略调优更重要。

笔记加载中…