Redis 明明满了,为什么还能继续写?真相太反直觉

21 阅读4分钟



想象一下,有一座小城,叫 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 的”。 也就是说:

  1. 命令开始执行
  2. Redis 为结果分配大量内存
  3. used_memory 瞬间飙升
  4. 直接远远超过 maxmemory
  5. 回收机制被触发

这时 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岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!