Redis Memory

Redis 内存满了,会崩溃还是自动清理?

答案不是二选一,而是取决于一个关键配置:maxmemory 有没有设置、设成什么策略。这篇文章把它讲透。

一句话结论

Redis 不会"凭空崩溃",但也不会"永远自己清理"

取决于 maxmemory 配置:

  1. 设了 maxmemory + 非 noeviction 策略 → Redis 自动清理(按策略淘汰旧键),服务继续跑,不崩溃。
  2. 设了 maxmemory + 默认 noeviction → Redis 不清理,但只是写命令返回 OOM 错误,进程不崩溃,读命令照常。
  3. 没设 maxmemory(=0,默认不限制) → Redis 一直吃内存,直到宿主机内存耗尽,被 Linux OOM Killer 杀掉或系统卡死——这才是"崩溃"的真实来源。

所以记住一句话:Redis 内存满 ≠ 崩溃。"崩溃"几乎都来自「没给 Redis 设上限」或「设了上限但缓存/副本缓冲把内存撑爆」。下面逐层拆开。

两种根本局面:看 maxmemory 这道分水岭

Redis 判定"内存满"的唯一标准是:当前数据占用的内存 ≥ maxmemory。而 maxmemory 默认是 0,意思是"不限制"。这就分出两条完全不同的命运。

写入触顶 maxmemory 数据内存 ≥ 阈值 分水岭:maxmemory 是否设置? 情形 A:设了 maxmemory(≠0) Redis 自己处理,不崩溃 → 按 maxmemory-policy 行动 非 noeviction:自动淘汰键 默认 noeviction:写命令报错 情形 B:未设置(=0 默认) Redis 不限制,宿主机兜底 → 内存持续增长不停止 → 系统内存耗尽 → OOM Killer 杀进程 / 卡死
图 1:内存满后的两条命运,由 maxmemory 这道分水岭决定

✅ 情形 A:设了 maxmemory

Redis 知道自己的"红线"在哪,数据内存触顶时会主动介入:要么按淘汰策略删旧键腾空间,要么(默认策略下)拒绝再写入但保住进程。无论哪种,进程不会崩。

💥 情形 B:没设 maxmemory

Redis 认为"内存无限大",来者不拒。直到把整台机器的物理内存吃光,由操作系统来决定谁去死——通常是 Redis 这个大内存户被 OOM Killer 优先干掉。这个崩溃锅不在 Redis 逻辑,在配置缺失。

设了 maxmemory 后:8 种淘汰策略怎么选

maxmemory-policy 决定 Redis 触顶时"淘汰谁"。8 种策略可分成 4 个家族,关键看两点:候选范围(所有键 vs 仅带 TTL 的键)和选择算法(LRU / LFU / TTL / Random)。

maxmemory-policy 候选=全部键 allkeys-* 家族 allkeys-lru · 最近最少用 allkeys-lfu · 最不常用 allkeys-random · 随机 候选=仅带 TTL 键 volatile-* 家族 volatile-lru · 最近最少用 volatile-lfu · 最不常用 volatile-random · 随机 volatile-ttl · 剩时最短 都不淘汰 noeviction(默认)
图 2:8 种策略的两大分类维度——候选范围(allkeys / volatile)与算法(LRU/LFU/TTL/Random),外加"谁都不淘汰"的 noeviction

8 种策略一览表

策略候选范围选择算法行为典型用途
noeviction—不淘汰默认 写命令报错,不删数据当缓存用时不选它;用于"数据神圣不可丢"
allkeys-lru全部键近似 LRU最常用 删最久未用纯缓存,所有键都可再生
allkeys-lfu全部键LFU 频率删访问频次最低热点明显、需长期保留热键
allkeys-random全部键随机随机删几乎不用,仅特殊测试
volatile-lru仅带 TTL 键近似 LRU在有过期键里删最久未用缓存+持久数据混存,只淘汰临时键
volatile-lfu仅带 TTL 键LFU 频率在过期键里删最不常用同上,但更重频率
volatile-random仅带 TTL 键随机在过期键里随机删极少用
volatile-ttl仅带 TTL 键剩余寿命删"最快过期"的键希望尽量留长生数据
关键区分:allkeys-* vs volatile-*
  • allkeys-*:候选是所有键。哪怕你设置了 TTL 的持久数据,也可能被淘汰。适合"Redis 就是缓存"的场景。
  • volatile-*:候选只限带 TTL 的键。没设过期时间的键(持久数据)永远安全。适合"缓存和持久数据放同一个 Redis"的场景。
  • ⚠️ 坑:用 volatile-* 时,如果所有键都没设 TTL,候选集为空 → Redis 退化为 noeviction,写命令直接报错!

noeviction(默认策略)的真相:报错,不是崩溃

很多人以为"内存满了 Redis 就崩",其实默认配置下它最温和:宁可报错也不删你的数据。

内存满 + noeviction 时

  • 所有写类命令(SET、LPUSH、HSET、EXPIRE…)返回错误 OOM command not allowed when used memory > 'maxmemory'
  • 读类命令(GET、HGET、LRANGE…)完全正常,能继续服务
  • 删除类命令(DEL、EXPIRE 后自然过期)正常,可手动腾空间
  • Redis 进程不崩溃、不重启

这意味着什么

  • 你的已存数据完好无损——这是 noeviction 的设计初衷:宁可写不进,也不乱删
  • 适合把 Redis 当数据库/消息队列而非纯缓存的场景
  • 代价是:应用层的写会批量失败,需要业务侧做好降级/重试/告警
  • 这不是崩溃,但体验上像"服务不可写",必须被监控到
⚠ 所以"会崩溃吗"的准确答案是 配置了 maxmemory(无论什么策略),Redis 进程都不会自己崩溃。崩溃只发生在「完全没配 maxmemory」导致被系统 OOM Killer 干掉,或「配了 maxmemory 但缓冲/副本把内存撑爆」的边界情况(见下文)。

自动清理靠什么算法:LRU / LFU / TTL / Random

当策略决定"要淘汰"时,具体挑哪个键?四类算法各有取舍。

LRU(最近最少使用)——最经典

淘汰"最久没被访问"的键。Redis 实现的是近似 LRU:不维护精确链表,而是随机采样 maxmemory-samples(默认 5)个键,从中挑最久未用的淘汰。牺牲一点精度换 CPU。

LFU(最不频繁使用)——Redis 4.0+ 更聪明

淘汰"访问频次最低"的键。每个键维护一个 8 位对数计数器(morris counter),访问时概率自增、随时间衰减。比 LRU 更能识别"曾经热但已冷"的键,适合长期热点场景。两个旋钮:lfu-log-factor(增长快慢,默认 10)、lfu-decay-time(衰减分钟数,默认 1)。

近似 LRU(采样淘汰) 随机采样 5 个键 A B C D E ↑ 其中最久未用者被淘汰 LFU(频率计数 + 衰减) 每个键一个访问计数器 K1 cnt=3 K2 cnt=9 K3 cnt=1 K3 最低 → 淘汰 ↑ 频次最低者被淘汰(随时间衰减)
图 3:近似 LRU(采样挑最久未用)与 LFU(按访问计数器挑最低)两种核心算法的对比

TTL 与 Random

✅ 工程选型直觉 纯缓存首选 allkeys-lru;有明显长期热点的选 allkeys-lfu;缓存与持久数据混存的选 volatile-lru/lfu;永远别用 random 当生产策略。

情形 B 的崩溃真相:不是 Redis 崩,是系统杀了它

当 maxmemory = 0(默认),Redis 不限制自己。内存使用会一路上涨,直到宿主机 RAM 耗尽。

时间 → 内存占用 宿主机物理内存上限 OOM Killer 触发 Redis 内存持续增长(maxmemory=0 不拦截)
图 4:未设 maxmemory 时,Redis 内存曲线一路逼近物理上限,越线后被操作系统 OOM Killer 终结
  • Redis 不设上限:客户端持续写入,used_memory 单调上升,Redis 自身不报警、不拦截。
  • 逼近物理内存:系统可用内存见底,开始用 swap,Redis 响应变慢(磁盘 IO)。
  • 内存耗尽:内核 OOM(Out-Of-Memory)子系统启动,按 oom_score 挑"最该死"的进程。
  • Redis 被 Kill:占用大内存的 Redis 通常是首选目标,进程直接被杀,表现为"突然崩溃/失联",日志里常见 Out Of Memory 或进程直接消失。
  • 恢复:若开了持久化(RDB/AOF)可重启加载;若是纯缓存则丢失全部热数据,瞬间击穿到后端 DB。
  • ⚠ 第二大坑:配了 maxmemory 也可能 OOM maxmemory 只计算数据内存,不包括:客户端输出缓冲、副本同步缓冲、AOF 重写缓冲、内存碎片。这些都会让 used_memory_rss(实际物理占用)超过 maxmemory。所以「设了 maxmemory 仍被 OOM」很常见——根因是缓冲/碎片,不是数据本身。

    内存满时,Redis 到底走哪条路(完整决策流)

    写入请求,内存达 maxmemory maxmemory=0 ? 未设置 否(=0)→ 不拦截 内存持续涨 → 系统 OOM → 进程被杀 是(≠0) 读命令?→ 正常返回 写命令 → 按 policy 判定 noeviction → 写返回 OOM 错误 (进程不崩,数据不丢) 其他策略 → 自动淘汰键,写入成功
    图 5:从"内存触顶"到"最终走向"的完整决策链路——核心分叉是 maxmemory 配没配、以及 policy 是不是 noeviction

    一句话串起来:配了 maxmemory,Redis 永远不崩(要么自清、要么报错);没配 maxmemory,崩溃风险来自操作系统,不在 Redis 自身逻辑。

    怎么配置、怎么监控:把崩溃挡在门外

    1. 设置上限与策略(推荐生产)

    # 临时生效(重启后失效) CONFIG SET maxmemory 2gb CONFIG SET maxmemory-policy allkeys-lru CONFIG SET maxmemory-samples 5 # LRU/LFU 采样精度 # 永久生效:写进 redis.conf maxmemory 2gb maxmemory-policy allkeys-lru

    2. 监控这几个指标(核心)

    指标 / 命令看什么异常信号
    INFO memory → used_memory / maxmemory数据内存 vs 上限两者接近 → 即将触顶
    INFO memory → mem_fragmentation_ratio碎片率 (rss/used)>1.5 碎片严重,rss 远超上限
    INFO stats → evicted_keys累计淘汰数持续增长 → 在频繁淘汰
    INFO stats → expired_keys自然过期数过低却频繁淘汰 → 策略不当
    CONFIG GET maxmemory-policy当前策略是 noeviction 且写报错 → 需调
    INFO clients → client_recent_max_output_buffer客户端缓冲大缓冲 → rss 超上限隐患

    3. 排查"明明配了 maxmemory 还崩"的思路

    1. 先看 used_memory 是否真超 maxmemory:若没超,说明崩在缓冲/碎片(rss 超了)。
    2. 看 mem_fragmentation_ratio:高碎片 → 考虑 activedefrag 主动碎片整理。
    3. 看 client_recent_max_output_buffer 和副本偏移:大查询/慢副本会撑爆 rss。
    4. 设 maxmemory 时留出缓冲(如机器 8G,maxmemory 设 4~5G),给缓冲与碎片留空间。
    5. 若是纯缓存,崩溃后重建可接受;若是数据源,必须开持久化 + 副本,并避免用会丢数据的策略。

    6 个常见误区,一次说清

    ❌ 误区 1:内存满了 Redis 就会崩

    错。只要设了 maxmemory,Redis 要么自动淘汰(其他策略),要么拒绝写入(noeviction),进程都不会崩。崩几乎都因没设上限。

    ✅ 真相 1

    崩溃的源头是操作系统 OOM,不是 Redis 逻辑。配置到位,Redis 极稳。

    ❌ 误区 2:noeviction 会崩溃

    错。noeviction 只是让写命令返回 OOM 错误,读照常,进程不死,数据不丢。

    ✅ 真相 2

    它是"最安全但不自动清理"的策略,适合数据不可丢场景,代价是写失败要业务兜底。

    ❌ 误区 3:设了 maxmemory 就绝不会 OOM

    错。缓冲、副本同步、AOF 重写、碎片都不计入 maxmemory,rss 仍可能撑爆内存。

    ✅ 真相 3

    maxmemory 限的是数据,不是进程总占用。要留缓冲 + 监控 rss。

    ❌ 误区 4:LRU 是精确 LRU

    错。Redis 用近似 LRU(采样)换性能,精度靠 maxmemory-samples 调。

    ✅ 真相 4

    想要更准用 LFU;想要更精确可调高 samples,但会更耗 CPU。

    ❌ 误区 5:volatile-* 一定安全

    不一定。若所有键都没设 TTL,候选集为空 → 退化为 noeviction,写直接报错。

    ✅ 真相 5

    用 volatile-* 前,确保要淘汰的键确实设了过期时间。

    ❌ 误区 6:淘汰影响读性能

    基本不影响读。淘汰是后台/写时触发,读命令正常;除非内存压力导致整体 swap 变慢。

    ✅ 真相 6

    真正拖慢的是"内存满→swap→磁盘 IO",不是淘汰本身。

    实战配置清单:照着做

    1. 必设 maxmemory:生产环境永远不要留 0。按机器内存的 50%~70% 设,给缓冲和碎片留余地。
    2. 选对策略:纯缓存 → allkeys-lru;长期热点 → allkeys-lfu;缓存+持久混存 → volatile-lru/lfu(且务必给缓存键设 TTL)。
    3. 别用 noeviction 当缓存:除非你明确要"写满即失败、数据不丢",并做好了业务降级。
    4. 监控 evicted_keys / used_memory / mem_fragmentation_ratio:设告警,触顶前介入。
    5. 给 rss 留缓冲:maxmemory 别顶满物理内存,防止缓冲/碎片导致系统 OOM。
    6. 大查询 & 慢副本是隐形杀手:监控 client output buffer 与副本延迟,避免它们把内存撑爆。
    7. 纯缓存可丢 → 崩溃可接受;若 Redis 是数据源 → 必须开 AOF/RDB + 副本,且避免会误删持久数据的策略。
    8. 压测验证:刻意灌满内存,观察是"平滑淘汰"还是"写报错",确认符合预期再上线。
    ✅ 终极一句话 Redis 内存满了不会自己崩溃:设了 maxmemory 就自动清理或优雅报错;没设才可能被系统 OOM 杀掉。把 maxmemory 配好、策略选对、监控到位,崩溃这个话题就和你没关系了。