想象一下,有一座小城,叫 Redis 城。城里有个超级重要的仓库,专门存各种快递包裹(也就是我们的 key-value 数据)。这个仓库的管理员,就是 Redis。
但问题来了:仓库不是无限大的。老板(也就是你在配置文件里)早早就跟 Redis 说清楚了:
意思翻译成人话就是:“老哥,你最多只能用 4GB,超了你自己想办法,至于扔谁,按我定的规矩来。”
Redis 点点头,说了句:“行,交给我。”
于是,故事开始了。
第一幕:一个客户端,悄悄塞进了新东西
某天,一个 Java 客户端跑过来,气喘吁吁地说:“Redis,来个 SET!”
在 Redis 眼里,这件事非常日常:
- 接收命令
- 解析协议
- 分配内存
- 把 key 和 value 放进字典
一切看起来风平浪静。
第二幕:Redis 下意识摸了摸自己的肚子
但 Redis 有个职业病。每执行一个可能增加内存的命令,它都会下意识地问自己一句: “我现在用的内存,会不会超标了?”
Redis 检查内存使用情况,流程大概是这样的(伪代码理解即可):
这里有几个面试官特别喜欢追问的点:
- 不是后台线程
- 不是定时任务
- 而是“命令执行路径上的同步检查”
也就是说:Redis 的内存回收,是被“新命令”推着走的。
第三幕:超线了,仓库必须清货
超过 maxmemory 会发生什么?一旦 Redis 发现:used_memory > maxmemory,它就会立刻进入一个状态: “不行了,必须马上清掉一些东西。”
这时,就轮到 maxmemory-policy 出场了。
淘汰策略,其实就是“丢谁最合适”
我们先用一张表,快速回顾一下面试必背内容,Redis 内存淘汰策略一览表如下所示:
重点一句话: Redis 并不是“扫一遍所有 key”,而是用近似 LRU / LFU 的采样算法,快速做决策。
第四幕:不断越界,又不断拉回
现在,来到面试题的核心逻辑。Redis 回收不是“一次性”的!
很多人误以为:“超过 maxmemory,Redis 一次性清到很低。”
这是错的。 真实情况是:Redis 在执行命令的过程中,反复经历下面这个循环:
执行命令 → 超过内存 → 淘汰一部分 → 回到线下 → 继续执行
你可以把它想象成一个人:
- 吃一口 → 发现胖了
- 跑两步 → 瘦一点
- 再吃一口 → 又胖了
- 再跑两步……
用一句话总结:Redis 是在 maxmemory 边界附近“来回横跳”的。
第五幕:一个命令,直接把仓库挤爆了
现在,故事迎来高潮。某天,一个程序员(不是你)写了这样一段代码:
假设:
- 每个 set 都是 百万级元素
- 交集结果非常大
- Redis 需要 一次性为 big:result 分配大量内存
会发生什么?
关键点来了:Redis 是“先分配内存,再检查 maxmemory 的”。 也就是说:
- 命令开始执行
- Redis 为结果分配大量内存
- used_memory 瞬间飙升
- 直接远远超过 maxmemory
- 回收机制被触发
这时 Redis 会不会“提前阻止”?
很多人会问:“Redis 能不能在执行前就判断,这个命令太大了,别执行?”
答案是:不能。
因为:
- Redis 并不知道交集结果有多大
- 很多命令的结果规模是运行时才知道的
所以才会出现这种情况:一个命令,瞬间把内存打穿。
Redis 的处理方式:尽力回收,但不保证成功
此时 Redis 的逻辑是:
只要 used_memory > maxmemory,就不断执行淘汰,直到:
- 内存回到线下 → 命令成功
- 已经没法淘汰 → 命令失败
什么时候会失败?比如:
- 策略是 volatile-lru
- 但所有 key 都没有过期时间
- Redis 根本“没得删”
这时就会返回错误:OOM command not allowed when used memory > 'maxmemory'
用一张流程表总结整个回收过程
Redis 内存回收完整流程表如下所示:
面试总结(直接背这一段)
最后,给你一段社招面试“标准答案级”总结:
Redis 的内存回收是一个由命令驱动的同步过程。
当客户端执行新命令导致内存使用超过 maxmemory 时,Redis 会在命令执行路径上立即触发回收逻辑,按照配置的淘汰策略删除部分 key,使内存使用量回落到 maxmemory 以下。
这个过程不是一次性的,而是随着新命令不断执行,在 maxmemory 边界附近反复触发。
对于可能产生大量结果的命令(如大集合计算),Redis 无法提前预估内存占用,可能会在执行过程中瞬间突破内存限制,只能通过事后回收或直接返回 OOM 错误来处理。
END
如果你能把这套故事:
- 命令驱动
- 同步检查
- 边界横跳
- 大命令风险
讲清楚,那这道 Redis 回收机制 的面试题,基本就是稳过。
我们下一篇文章,接着聊,再见。
我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!