Redis 内存满了怎么办:八种淘汰策略怎么选

米阳 2025-2-8 4 2/8

Redis 内存到上限之后的行为,由 maxmemory-policy 决定。选错了,轻则缓存命中率下降,重则数据丢失、请求变慢。

先看现状

redis-cli INFO memory | grep -E 'used_memory_human|maxmemory_human|maxmemory_policy'
redis-cli INFO stats | grep evicted_keys

evicted_keys 持续增长说明在频繁淘汰——要么内存不够,要么策略不合适。

八种策略

策略 淘汰范围 说明
noeviction 不淘汰 写满报错,默认值
allkeys-lru 所有 key 淘汰最久未使用的
allkeys-lfu 所有 key 淘汰访问频率最低的
allkeys-random 所有 key 随机淘汰
volatile-lru 设了过期时间的 只淘汰有 TTL 的
volatile-lfu 设了过期时间的 同上,按频率
volatile-random 设了过期时间的 同上,随机
volatile-ttl 设了过期时间的 优先淘汰剩余时间短的

怎么选

Redis 只做缓存(最常见):allkeys-lru。所有 key 都是可丢的,按最近使用情况淘汰,命中率最高。

Redis 既做缓存又存不能丢的数据(比如队列、分布式锁):noeviction。写满直接报错,逼你扩容或清理,不会悄悄删掉重要数据。这时候缓存部分应该单独用一个实例。

有明确 TTL 的临时数据:volatile-lru。只淘汰设了过期时间的,没设 TTL 的永久数据不动。风险是如果所有 key 都没设 TTL,退化成 noeviction 报错。

热点差异明显:allkeys-lfu。LFU 按访问频率淘汰,比 LRU 更能保住「一直有人访问但不够新」的热点。缺点是 LFU 计数需要时间积累,新 key 容易吃亏(Redis 4.0 之后有计数衰减和初始计数缓解)。

LRU 不是真的 LRU

Redis 的 LRU 是近似实现:采样若干个 key,淘汰其中最久未使用的,不是全局精确排序。这样省内存也快,够用。

maxmemory-samples 默认 5,调大更接近真实 LRU,但更耗 CPU。10 是个不错的折中。

内存该设多大

留出余量。Redis 在 fork 做 RDB 快照时,写时复制会额外占用内存,极端情况接近翻倍。所以:

maxmemory = 物理内存 × 0.6 ~ 0.7

还要注意 used_memory 之外的碎片率:

redis-cli INFO memory | grep mem_fragmentation_ratio

大于 1.5 说明碎片多,可以开 activedefrag yes,或者挑低峰重启。

我的配置

maxmemory 4gb
maxmemory-policy allkeys-lru
maxmemory-samples 10
timeout 300
tcp-keepalive 60

缓存实例用 allkeys-lru;队列和锁用另一个实例,noeviction + AOF。分开之后,两种用途互不干扰,也更容易定位问题。

一个教训

曾经把验证码、session 和缓存放在同一个实例,策略设成 allkeys-lru。某天缓存写入量暴增,把 session 全淘汰了,所有用户被踢下线。

结论:不同用途的 key 分实例,比调策略有效得多。

- THE END -

米阳

2月08日16:15

最后修改:2025年2月8日
0

非特殊说明,本博所有文章均为博主原创。

共有 0 条评论