Redis 面试题 #33

Redis 内存用完了
会发生什么?

内存上限触达后的行为、两种应对策略、8 种淘汰策略详解

💡
核心结论

当 Redis 内存达到 maxmemory 上限时,行为取决于你的配置策略——
未配置淘汰机制:写命令报错,读命令仍可正常执行;
配置了淘汰机制:Redis 自动淘汰旧数据后继续写入,通常用于作缓存。

🔀
两种应对场景

场景一:返回错误(默认)

配置:maxmemory-policy noeviction

Redis 拒绝所有会增加内存的写操作(SET、LPUSH、HSET…), 并返回 OOM command not allowed when used memory > 'maxmemory' 错误。

读命令(GET、LRANGE…)仍可正常返回。
写命令会报错,应用层必须捕获并处理。

场景二:淘汰旧数据(缓存模式)

配置淘汰策略,如 allkeys-lru

Redis 在写入时自动驱逐"最不值得保留"的 Key, 腾出空间后再写入新数据,对调用方透明无感知。

✅ 适合作为缓存层使用,数据本身可从数据库重新加载。
⚠️ 重要持久数据不要开淘汰,否则数据会被悄悄删除。

✅ 读命令

GET / LRANGE / HGET 等
内存满了依然正常返回

❌ 写命令

SET / LPUSH / INCR 等
noeviction 时直接报 OOM 错误

Redis 内存满时的执行流程
1

客户端发送写命令

SET key value,Redis 准备处理该命令。

2

检查当前内存使用量

Redis 判断当前 used_memory 是否超过 maxmemory 设置的阈值。

3

读取 maxmemory-policy 策略

根据配置的策略决定下一步行为(noeviction / lru / lfu / random / ttl)。

4a

noeviction → 直接报错

返回 -OOM command not allowed when used memory > 'maxmemory',命令不执行。

4b

其他策略 → 执行淘汰

按策略找到候选 Key 并删除,释放内存,然后继续执行写命令,对客户端透明。

⚙️
如何配置内存上限

redis.conf 中设置,或使用 CONFIG SET 动态修改:

# redis.conf 静态配置 maxmemory 2gb # 设置上限为 2GB(支持 kb/mb/gb) maxmemory-policy allkeys-lru # 设置淘汰策略 # 动态修改(无需重启,立即生效) 127.0.0.1:6379> CONFIG SET maxmemory 2gb 127.0.0.1:6379> CONFIG SET maxmemory-policy allkeys-lru # 查看当前配置 127.0.0.1:6379> CONFIG GET maxmemory 127.0.0.1:6379> CONFIG GET maxmemory-policy
⚠️

注意:maxmemory 默认为 0

如果不设置,Redis 不限制内存使用,会一直分配直到耗尽操作系统可用内存,可能导致 OOM Killer 直接杀掉进程!生产环境务必设置此参数。

📋
8 种淘汰策略详解
策略名称 淘汰范围 淘汰算法 适用场景 推荐
noeviction 不淘汰 直接报错 数据库模式,不允许丢数据 默认
allkeys-lru 所有 Key LRU(最近最少使用) 通用缓存,热点数据分布不均匀 ★ 推荐
volatile-lru 设了 TTL 的 Key LRU 混合存储,部分数据必须保留 按需
allkeys-lfu 所有 Key LFU(最少频率使用) 访问频率差异明显的缓存 ★ 推荐
volatile-lfu 设了 TTL 的 Key LFU 有 TTL 的热点数据缓存 按需
allkeys-random 所有 Key 随机删除 所有 Key 访问频率相似 慎用
volatile-random 设了 TTL 的 Key 随机删除 TTL Key 的随机淘汰 慎用
volatile-ttl 设了 TTL 的 Key TTL 最短优先 希望快过期的先被清理 按需
ℹ️

LRU vs LFU 怎么选?

LRU(Least Recently Used):淘汰最长时间没有被访问的 Key,适合热点数据有明显"时间局部性"的场景。
LFU(Least Frequently Used):淘汰访问次数最少的 Key(Redis 4.0+),适合热点固定、长期高频访问的场景,不会因为"偶发"访问而保留冷数据。

🖥️
noeviction 模式下的错误演示

当内存达到上限,写命令会立即收到 OOM 错误:

# 假设 maxmemory = 100mb,已满 127.0.0.1:6379> SET new_key "hello" (error) OOM command not allowed when used memory > 'maxmemory' 127.0.0.1:6379> LPUSH mylist item (error) OOM command not allowed when used memory > 'maxmemory' # 但读命令依然正常工作! 127.0.0.1:6379> GET existing_key "existing_value" # ✅ 正常返回 127.0.0.1:6379> DBSIZE (integer) 1024000 # ✅ 正常返回
🎯
最佳实践建议

纯缓存场景 → 用 allkeys-lru 或 allkeys-lfu

所有数据都是缓存,都可以从数据库重建,推荐开启 allkeys-lru(或更精准的 allkeys-lfu),不用担心数据丢失。

混合存储场景 → 用 volatile-lru,为持久数据不设 TTL

必须持久化的 Key 不设过期时间,Redis 只会淘汰设了 TTL 的缓存 Key,核心数据得到保护。

设置合理的 maxmemory,留 10~20% 余量

如机器有 8GB,设置 maxmemory 6~7GB,避免极端情况下内存抖动触发 OOM。

监控 evicted_keys 指标

通过 INFO stats | grep evicted_keys 观察淘汰频率。淘汰过于频繁说明内存不足,考虑扩容或优化数据结构。

监控命令速查

INFO memory — 查看内存使用详情(used_memory、maxmemory 等)
INFO stats | grep evicted — 查看累计淘汰 Key 数量
MEMORY USAGE <key> — 查询某个 Key 占用的字节数

🗂️
三句话总结
🚫

默认行为

noeviction 策略,写命令报 OOM 错误,读命令正常执行

♻️

缓存模式

配置淘汰策略后,Redis 自动驱逐旧 Key,透明地腾出空间

🎛️

策略选择

纯缓存用 allkeys-lru/lfu,混合存储用 volatile-lru