Redis 持久化、过期删除、淘汰策略与内存碎片全解析

Redis 持久化、过期删除、淘汰策略与内存碎片全解析 -- pd的后端笔记

文章目录

    • [Redis 持久化、过期删除、淘汰策略与内存碎片全解析 -- pd的后端笔记](#Redis 持久化、过期删除、淘汰策略与内存碎片全解析 -- pd的后端笔记)
  • [🎯 核心问题](#🎯 核心问题)
  • [🧠 一、先建立整体认知:Redis 的"内存治理"全景图](#🧠 一、先建立整体认知:Redis 的“内存治理”全景图)
    • [📊 一张表先把几个概念区分开](#📊 一张表先把几个概念区分开)
  • [🚀 二、Redis 的持久化机制有哪些?](#🚀 二、Redis 的持久化机制有哪些?)
    • [1. 一句话先回答](#1. 一句话先回答)
    • [2. RDB:快照型持久化](#2. RDB:快照型持久化)
      • [✅ 优点](#✅ 优点)
      • [⚠️ 缺点](#⚠️ 缺点)
    • [3. AOF:日志型持久化](#3. AOF:日志型持久化)
      • [Redis 7.0 之后的补充](#Redis 7.0 之后的补充)
    • [4. RDB 和 AOF 怎么选?](#4. RDB 和 AOF 怎么选?)
      • [📊 对比表](#📊 对比表)
      • [✅ 生产建议](#✅ 生产建议)
  • [🔧 三、Redis 在生成 RDB 文件时如何处理请求?](#🔧 三、Redis 在生成 RDB 文件时如何处理请求?)
    • [1. 先给结论](#1. 先给结论)
    • [2. 处理流程图](#2. 处理流程图)
    • [3. 为什么还能继续处理请求?](#3. 为什么还能继续处理请求?)
    • [4. 那新写入的数据会不会丢?](#4. 那新写入的数据会不会丢?)
    • [5. Copy-On-Write 到底在干什么?](#5. Copy-On-Write 到底在干什么?)
      • [⚠️ 工程影响](#⚠️ 工程影响)
    • [6. `SAVE` 和 `BGSAVE` 的区别](#6. SAVEBGSAVE 的区别)
      • [✅ 面试回答建议](#✅ 面试回答建议)
  • [⏰ 四、Redis 数据过期后的删除策略是什么?](#⏰ 四、Redis 数据过期后的删除策略是什么?)
    • [1. 惰性删除(lazy expiration)](#1. 惰性删除(lazy expiration))
    • [2. 定期删除(active expiration)](#2. 定期删除(active expiration))
    • [3. 为什么 Redis 不做"定时器精准删除"?](#3. 为什么 Redis 不做“定时器精准删除”?)
    • [4. 过期 key 在主从/AOF 中怎么处理?](#4. 过期 key 在主从/AOF 中怎么处理?)
    • [📊 过期删除策略总结表](#📊 过期删除策略总结表)
      • [✅ 面试标准答法](#✅ 面试标准答法)
  • [🗑️ 五、Redis 中有哪些内存淘汰策略?](#🗑️ 五、Redis 中有哪些内存淘汰策略?)
    • [1. 先区分"过期删除"和"内存淘汰"](#1. 先区分“过期删除”和“内存淘汰”)
    • [2. 经典高频答案:8 种策略](#2. 经典高频答案:8 种策略)
    • [3. 如果按最新官方文档看:Redis 8.6 新增了 LRM](#3. 如果按最新官方文档看:Redis 8.6 新增了 LRM)
    • [4. LRU、LFU、LRM、TTL 分别适合什么场景?](#4. LRU、LFU、LRM、TTL 分别适合什么场景?)
    • [5. 生产里最常见的选择](#5. 生产里最常见的选择)
      • [场景 1:纯缓存](#场景 1:纯缓存)
      • [场景 2:很多 key 不允许被淘汰,只有临时 key 能删](#场景 2:很多 key 不允许被淘汰,只有临时 key 能删)
      • [场景 3:热点相对稳定](#场景 3:热点相对稳定)
    • [6. 一个容易踩坑的点](#6. 一个容易踩坑的点)
      • [✅ 面试简答模板](#✅ 面试简答模板)
  • [🧩 六、Redis 的内存碎片化是什么?如何优化?](#🧩 六、Redis 的内存碎片化是什么?如何优化?)
    • [1. 什么叫内存碎片化?](#1. 什么叫内存碎片化?)
    • [2. 为什么 Redis 容易出现碎片?](#2. 为什么 Redis 容易出现碎片?)
    • [3. 该看哪些指标?](#3. 该看哪些指标?)
    • [4. 如何优化内存碎片?](#4. 如何优化内存碎片?)
    • [✅ 方法 1:开启 active defrag](#✅ 方法 1:开启 active defrag)
    • [✅ 方法 2:做对象模型优化,减少 churn](#✅ 方法 2:做对象模型优化,减少 churn)
    • [✅ 方法 3:给持久化留足内存余量](#✅ 方法 3:给持久化留足内存余量)
    • [✅ 方法 4:必要时做 `MEMORY PURGE`](#✅ 方法 4:必要时做 MEMORY PURGE)
    • [✅ 方法 5:在合适窗口做重启/迁移/主从切换](#✅ 方法 5:在合适窗口做重启/迁移/主从切换)
    • [📊 优化思路总结表](#📊 优化思路总结表)
      • [✅ 面试回答建议](#✅ 面试回答建议)
  • [🧾 七、Redis 的虚拟内存(VM)机制是什么?](#🧾 七、Redis 的虚拟内存(VM)机制是什么?)
    • [1. 先说正确版本结论](#1. 先说正确版本结论)
    • [2. 它当年想解决什么问题?](#2. 它当年想解决什么问题?)
    • [3. 为什么后来被废弃?](#3. 为什么后来被废弃?)
    • [4. 今天该怎么理解这道题?](#4. 今天该怎么理解这道题?)
  • [⚠️ 八、这些问题背后真正的工程风险是什么?](#⚠️ 八、这些问题背后真正的工程风险是什么?)
  • [📊 九、面试速查表](#📊 九、面试速查表)
    • [1. 六个问题的快速答案](#1. 六个问题的快速答案)
    • [2. 一段更像面试现场的话术](#2. 一段更像面试现场的话术)
  • [✅ 十、总结](#✅ 十、总结)
  • [🔗 参考资料](#🔗 参考资料)

🎯 核心问题

Redis 为什么既快又容易成为高频面试题?

因为它看起来只是一个"内存数据库",但背后真正考察的是 4 件事:

  1. 数据会不会丢:对应持久化机制。
  2. 内存会不会爆:对应过期删除与淘汰策略。
  3. 内存为什么看起来越用越多:对应内存碎片化。
  4. 历史方案为什么被废弃:对应 Redis 的 VM(Virtual Memory)机制。

如果只停留在"RDB 是快照、AOF 是日志"这个层面,其实还不够。

下面我们从一个更工程化的视角,把这几个点串成一条完整链路:

  • 数据写进 Redis 后,如何落盘?
  • 生成 RDB 文件时,Redis 还能不能继续处理请求?
  • key 过期了,Redis 会不会立刻删?
  • 内存满了,Redis 到底删谁?
  • 明明 key 不多,为什么 RSS 还是很大?
  • Redis 以前的 VM 到底是什么,为什么后来不用了?

🧠 一、先建立整体认知:Redis 的"内存治理"全景图

我们先别急着背概念,先看 Redis 对数据和内存的完整处理链路。
RDB
AOF
RDB+AOF






客户端写入数据
数据进入内存
是否开启持久化
按规则触发快照
追加写命令到 AOF
同时保留快照与操作日志
key 是否设置 TTL
惰性删除 + 定期删除
常驻内存
内存是否达到 maxmemory
继续提供服务
触发淘汰策略
是否存在碎片化
active defrag / purge / 重启迁移
稳定运行

📊 一张表先把几个概念区分开

主题 解决的问题 什么时候触发 代价 / 风险
RDB 宕机后如何恢复数据 定时、BGSAVE、主从全量同步 可能丢最近一次快照后的数据;fork 有成本
AOF 提高数据持久性 每次写命令后追加日志 文件更大,恢复更慢,存在磁盘 I/O 压力
过期删除 已过期 key 怎么清理 访问时 / 周期扫描时 删除不一定"立刻"发生
内存淘汰 内存满了删谁 达到 maxmemory 可能影响命中率,写入延迟会上升
内存碎片 为什么 RSS 高于真实数据量 分配器释放不完全、频繁 churn 物理内存浪费,fork 期间更危险
VM(历史) 以前如何把冷数据换出到磁盘 超内存时尝试 swap value 性能复杂、收益不稳定,已废弃

到这里你可以把它理解成一句话:

持久化解决"断电后还能不能回来",过期和淘汰解决"内存满了怎么办",碎片化解决"为什么看起来没满却已经很危险"。


🚀 二、Redis 的持久化机制有哪些?

1. 一句话先回答

Redis 的主流持久化机制有两种:

  • RDB(Redis Database) :某个时间点的内存快照
  • AOF(Append Only File) :按顺序记录写命令日志

在工程实践里,常见状态其实有 4 种:

模式 说明 适用场景
不开启持久化 纯内存,重启即丢 纯缓存、可重建数据
只开 RDB 定时快照 备份、恢复快、能接受分钟级数据丢失
只开 AOF 记录写操作 对数据安全要求更高
RDB + AOF 两者同时开 生产最常见,AOF 保完整性,RDB 保恢复效率

2. RDB:快照型持久化

RDB 的核心思想是:

在某个时间点,把当前整个数据集序列化成一个二进制快照文件,比如 dump.rdb

典型触发方式:

  • save 900 1
  • save 300 10
  • save 60 10000
  • 手动执行 SAVE / BGSAVE
  • 主从复制全量同步时生成 RDB

✅ 优点

  • 文件紧凑,适合备份。
  • 恢复速度快,直接把快照装入内存即可。
  • 对运行时 I/O 压力相对可控。

⚠️ 缺点

  • 快照之间的数据可能丢失。
  • fork 子进程有成本,内存大时会抖一下。
  • 写流量高时,copy-on-write 会放大内存消耗。

3. AOF:日志型持久化

AOF 的核心思想是:

每当 Redis 执行一条会修改数据集的命令,就把这条命令追加到 AOF 文件里;重启时按顺序回放这些命令,恢复数据。

常见刷盘策略:

配置 含义 数据安全性 性能
appendfsync always 每次写都刷盘 最高 最慢
appendfsync everysec 每秒刷一次 较高,最多丢约 1 秒 常用平衡方案
appendfsync no 由 OS 决定何时刷盘 最弱 最快

Redis 7.0 之后的补充

如果你面试的是较新的 Redis 版本,AOF 还可以多说一句:

  • 自 Redis 7.0.0 起,官方使用 multi-part AOF
  • AOF 不再只是单个大文件,而是拆成:
    • base file
    • incremental files
    • manifest

这会让 AOF rewrite 的切换更安全,也更容易管理增量变更。

4. RDB 和 AOF 怎么选?

📊 对比表

对比项 RDB AOF
落盘方式 全量快照 增量日志
数据恢复速度 慢一些
文件大小 相对小 相对大
数据完整性 较弱 较强
对磁盘写压力 较低 较高
适合场景 备份、灾备 高可靠业务

✅ 生产建议

  • 纯缓存场景:可以不持久化,或者只做 RDB。
  • 一般业务场景 :推荐 RDB + AOF(everysec)
  • 极致可靠场景:AOF 更重要,但要接受更高 I/O 成本。

🔧 三、Redis 在生成 RDB 文件时如何处理请求?

这是高频考点,因为它考察的是:

  • Redis 是否会阻塞?
  • 快照期间数据一致性怎么保证?
  • 为什么大实例做 BGSAVE 会抖?

1. 先给结论

如果执行的是 BGSAVE,Redis 主进程通常还能继续处理客户端请求。

它的做法不是"主线程一边写快照一边处理请求",而是:

  • 主进程 fork 一个子进程
  • 子进程负责把当前内存视图写成 RDB
  • 主进程继续处理读写请求

但这里有两个重要补充:

  1. fork 本身会有短暂阻塞成本
  2. 快照期间如果主进程继续写数据,会触发 Copy-On-Write(COW),带来额外内存开销。

2. 处理流程图

dump.rdb RDB子进程 Redis主进程 Client dump.rdb RDB子进程 Redis主进程 Client 发起写请求/读请求 触发 BGSAVE fork 子进程 按 fork 时刻的内存视图写临时 RDB 继续处理新请求 新写请求触发 COW 写完临时文件 通知完成 原子替换旧 RDB 文件

3. 为什么还能继续处理请求?

因为 BGSAVE 的核心是 父子进程职责分离

  • 子进程:负责把"fork 那一刻"的数据集写入磁盘。
  • 父进程:继续接收客户端请求。

这意味着:

  • 读请求:可以继续处理。
  • 写请求:也可以继续处理。
  • 但写入发生在 fork 之后的那些变更,不属于本轮 RDB 快照内容。

这点非常关键。

RDB 快照记录的是"某一瞬间的内存状态",不是"快照持续期间不断变化的最终状态"。

4. 那新写入的数据会不会丢?

分情况看:

  • 如果只开启 RDB,那么这些新写入的数据要等下一次快照才会被落盘。
  • 如果同时开启了 AOF,那么这些写命令还会继续进入 AOF,因此通常能更完整保留。

5. Copy-On-Write 到底在干什么?

fork 完成后,父子进程最开始会共享同一批物理内存页。

当主进程收到新写请求时:

  • 如果改动到了某个共享页
  • OS 会复制这个页
  • 父进程改自己的副本
  • 子进程继续读旧页,保证快照一致

这就是 Copy-On-Write

⚠️ 工程影响

如果你的 Redis:

  • 内存很大
  • 写入非常频繁
  • 正在做 BGSAVEBGREWRITEAOF

那么就会出现两类风险:

风险 原因 结果
短暂卡顿 fork 要复制页表 RT 抖动、请求尖刺
内存放大 COW 复制被修改的内存页 峰值内存暴涨,甚至 OOM

所以生产环境里常说:

做快照不是没有成本,而是把成本从"长时间阻塞"换成了"短暂 fork + 可能的 COW 放大"。

6. SAVEBGSAVE 的区别

命令 行为 是否阻塞
SAVE 主线程直接执行快照 是,严重阻塞
BGSAVE fork 子进程后台生成 主流程基本不阻塞,但 fork 有代价

✅ 面试回答建议

如果被问"Redis 生成 RDB 时如何处理请求",推荐这样答:

Redis 通常通过 BGSAVE 生成 RDB。主进程会先 fork 出子进程,由子进程把 fork 时刻的数据视图写入临时 RDB 文件,主进程继续处理读写请求。读请求不受影响,写请求也能继续执行;但写入修改不会进入当前这份快照,而是通过 Copy-On-Write 保证子进程看到的是一致快照。代价是 fork 有短暂阻塞,大实例和高写入场景下还可能出现 COW 带来的内存放大。


⏰ 四、Redis 数据过期后的删除策略是什么?

很多人会误以为:

key 到了 TTL,Redis 就会"立刻"删除。

其实不是。

Redis 为了避免把大量 CPU 时间都花在"扫过期 key"上,采用的是 惰性删除 + 定期删除 的组合策略。

1. 惰性删除(lazy expiration)

意思是:

  • 当客户端访问某个 key 时
  • Redis 先检查这个 key 有没有过期
  • 如果已过期,就先删除,再返回不存在

优点

  • 很省 CPU
  • 只有访问到时才处理

缺点

  • 如果某个已过期 key 之后再也没人访问
  • 那它会继续占着内存一段时间

2. 定期删除(active expiration)

只靠惰性删除不够,因为很多过期 key 可能永远没人再访问。

所以 Redis 会周期性抽样检查带过期时间的 key:

  • 随机抽一批带 TTL 的 key
  • 删除其中已经过期的 key
  • 如果过期比例很高,就继续扫一轮

这本质上是一个:

CPU 成本和内存回收效率之间的折中。

3. 为什么 Redis 不做"定时器精准删除"?

因为如果给每个 key 都挂一个独立定时任务:

  • key 数量一大,调度成本非常高
  • 时间轮 / 小顶堆也会引入额外内存和管理开销
  • 高并发场景下会把 Redis 主线程拖慢

所以 Redis 选择了更实用的折中方案:

  • 访问时顺手删
  • 后台定期抽样删

4. 过期 key 在主从/AOF 中怎么处理?

这里是一个经常被忽略的细节。

为了保证复制一致性:

  • 主节点负责"裁定"过期
  • 当主节点确认某个 key 过期后,会合成一条 DEL
  • 这个 DEL 会传播给 AOF 和副本

也就是说,Redis 不依赖主从时钟绝对一致来分别删除同一个 key,而是把删除动作尽量集中在主节点控制。

📊 过期删除策略总结表

策略 触发时机 优点 缺点
惰性删除 key 被访问时 CPU 友好 容易让过期 key 滞留内存
定期删除 后台周期扫描 能清理冷过期 key 要消耗额外 CPU
定时器删除 理论可行,Redis 不采用 删除及时 调度成本太高

✅ 面试标准答法

Redis 的过期删除不是单一策略,而是惰性删除和定期删除结合。惰性删除指访问 key 时发现过期就删除;定期删除指 Redis 周期性随机抽样带 TTL 的 key,把过期的清掉。这样做是为了在 CPU 开销和内存回收效率之间做平衡。


🗑️ 五、Redis 中有哪些内存淘汰策略?

1. 先区分"过期删除"和"内存淘汰"

这两个概念很容易混。

  • 过期删除:key 自己到了 TTL。
  • 内存淘汰 :Redis 达到 maxmemory 后,不得不删一些 key 给新数据腾空间。

所以判断顺序通常是:

  1. 先看 key 有没有过期。
  2. 如果实例整体内存还不够,再触发淘汰策略。

2. 经典高频答案:8 种策略

如果你面对的是常规面试题,大多数场景回答下面这 8 种就够了:

策略 含义
noeviction 不淘汰,写请求直接报错
volatile-lru 只在设置了过期时间的 key 中淘汰最近最少使用的
volatile-lfu 只在设置了过期时间的 key 中淘汰最不经常使用的
volatile-random 只在设置了过期时间的 key 中随机淘汰
volatile-ttl 只在设置了过期时间的 key 中优先淘汰剩余 TTL 更短的
allkeys-lru 在所有 key 中淘汰最近最少使用的
allkeys-lfu 在所有 key 中淘汰最不经常使用的
allkeys-random 在所有 key 中随机淘汰

3. 如果按最新官方文档看:Redis 8.6 新增了 LRM

这里给你一个具体日期,方便你回答"最新版本"时不出错:

  • Redis Open Source 8.6.0 发布于 2026 年 2 月
  • 这一版官方新增了两种 LRM(Least Recently Modified) 策略:
    • volatile-lrm
    • allkeys-lrm

所以如果你问"截至 2026-04 Redis 官方有哪些淘汰策略",更完整的答案是:

分类 策略
不淘汰 noeviction
仅淘汰带 TTL 的 key volatile-lru / volatile-lfu / volatile-random / volatile-ttl / volatile-lrm
可淘汰所有 key allkeys-lru / allkeys-lfu / allkeys-random / allkeys-lrm

一个容易忽略的细节

LRU / LFU / LRM 在 Redis 里都不是"全量精确排序后再淘汰",而是基于采样的近似算法

这意味着:

  • Redis 不会为了找"全局最老 / 最不常用 / 最久未修改"的 key 而扫描全库
  • 这样 CPU 成本更低
  • 但淘汰结果是"足够好",不是"数学意义上的绝对最优"

4. LRU、LFU、LRM、TTL 分别适合什么场景?

策略族 核心思想 更适合什么业务
LRU 最近不用的先删 热点会快速变化的缓存
LFU 访问频率低的先删 热点稳定、长尾明显的缓存
LRM 最近很久没被修改的先删 读多写少,且"最近更新更重要"的业务
TTL 快过期的先删 生命周期已经由业务 TTL 明确表达
Random 随机删 测试或低要求场景

5. 生产里最常见的选择

场景 1:纯缓存

推荐:

conf 复制代码
maxmemory 4gb
maxmemory-policy allkeys-lru

原因:

  • 不依赖每个 key 都有 TTL
  • 命中率通常比较稳
  • 运维成本低

场景 2:很多 key 不允许被淘汰,只有临时 key 能删

推荐:

conf 复制代码
maxmemory-policy volatile-lru

前提是:

  • 你必须真的给"可淘汰 key"设置了 TTL
  • 否则 Redis 可选的淘汰对象会很少,甚至无 key 可删

场景 3:热点相对稳定

推荐:

conf 复制代码
maxmemory-policy allkeys-lfu

6. 一个容易踩坑的点

如果你设置的是:

conf 复制代码
maxmemory-policy noeviction

那么当内存打满后:

  • 读请求还能继续
  • 但写请求会报错

这对"把 Redis 当数据库用"的业务尤其危险。

✅ 面试简答模板

Redis 的内存淘汰策略是在达到 maxmemory 后触发的。经典面试题一般答 8 种:noevictionvolatile-lruvolatile-lfuvolatile-randomvolatile-ttlallkeys-lruallkeys-lfuallkeys-random。如果按最新官方版本看,截至 Redis 8.6.0(2026 年 2 月)还新增了 volatile-lrmallkeys-lrm。生产里最常见的是 allkeys-lruallkeys-lfu


🧩 六、Redis 的内存碎片化是什么?如何优化?

1. 什么叫内存碎片化?

用最直白的话讲:

Redis 逻辑上已经释放了一部分内存,但操作系统层面看到的 RSS 还是很高,或者分配器手里的内存块不够整齐,导致这些内存没能高效返还给 OS,也没被后续分配很好复用。

所以我们会看到一种现象:

  • used_memory 看起来没那么高
  • used_memory_rss 很高
  • mem_fragmentation_ratio 也偏大

这就叫碎片化

2. 为什么 Redis 容易出现碎片?

核心原因通常有这几个:

(1)频繁创建/删除不同大小的对象

比如:

  • 大量短生命周期 key
  • list / hash / zset 大小波动明显
  • 批量写入后又批量删除

这会让分配器手里的内存块变得不规整。

(2)大 key 修改频繁

大对象一旦经常扩容、缩容、重写,内存页更容易出现空洞。

(3)fork + Copy-On-Write 的放大效应

在执行 BGSAVE / BGREWRITEAOF 时:

  • Redis 会 fork
  • 写流量大时很多页被复制
  • 峰值 RSS 往往比平时更高

(4)内存分配器行为

Redis 常配合 jemalloc 使用。

分配器为了性能会缓存、复用内存页,但这不等于它会立刻把空闲页还给操作系统。

3. 该看哪些指标?

📊 建议先看这几个

bash 复制代码
redis-cli INFO memory
redis-cli MEMORY STATS
redis-cli MEMORY DOCTOR

重点关注:

指标 含义 怎么看
used_memory Redis 真实分配给数据结构的内存 逻辑数据量
used_memory_rss 进程在 OS 层面占用的物理内存 真实 RSS
mem_fragmentation_ratio used_memory_rss / used_memory 粗粒度碎片比
allocator_frag_ratio 分配器层面的真实外部碎片指标 更值得关注
allocator_rss_ratio 分配器保留但未返还 OS 的程度 观察归还能力
active_defrag_running 当前是否正在主动碎片整理 观察 defrag 是否生效

一个容易误判的细节

不要只看 mem_fragmentation_ratio 一个数。

因为官方文档也强调:

  • 如果绝对字节量不大
  • 即使 ratio 看起来偏高
  • 也未必是大问题

所以你要同时看:

  • mem_fragmentation_ratio
  • mem_fragmentation_bytes
  • allocator_frag_ratio
  • used_memory_peak

4. 如何优化内存碎片?

✅ 方法 1:开启 active defrag

这是 Redis 官方提供的主动碎片整理机制。

conf 复制代码
activedefrag yes

如果要更细调,还可以配合这些参数:

conf 复制代码
active-defrag-ignore-bytes 100mb
active-defrag-threshold-lower 10
active-defrag-threshold-upper 100
active-defrag-cycle-min 1
active-defrag-cycle-max 25
active-defrag-max-scan-fields 1000

适用场景

  • 长期运行实例
  • key churn 很高
  • 删除多、对象大小波动大

注意点

  • 会额外占用 CPU
  • 不是"越 aggressive 越好"

✅ 方法 2:做对象模型优化,减少 churn

比起事后整理,更推荐的做法是从数据模型上减碎片

比如:

  • 避免超大 key
  • 避免频繁把一个大 value 整体覆盖
  • 尽量使用更紧凑的数据结构
  • 让 key 生命周期更均匀,不要突然海量创建、海量删除

✅ 方法 3:给持久化留足内存余量

这点非常重要。

如果你的 Redis 既要高写入,又要做 BGSAVE/BGREWRITEAOF,一定要预留足够内存。

原因是:

  • 碎片化会抬高 RSS
  • fork + COW 又会进一步放大峰值内存
  • 最终可能不是数据本身太大,而是碎片 + COW 把你打爆

所以 maxmemory 不要贴着机器物理内存上限设。

✅ 方法 4:必要时做 MEMORY PURGE

某些场景下,可以尝试:

bash 复制代码
redis-cli MEMORY PURGE

它的作用更偏向于:

  • 让分配器尝试把可归还的页返还给操作系统
  • 适合删除高峰过后,RSS 长时间不下来的场景

但要注意:

  • 它不是万能按钮
  • 对所有碎片问题都不一定立竿见影

✅ 方法 5:在合适窗口做重启/迁移/主从切换

如果实例长期运行、碎片严重、业务允许短切换:

  • 通过主从切换
  • 滚动重启
  • 迁移到新实例

往往是最直接的"碎片清零"手段。

📊 优化思路总结表

方案 本质 优点 代价
activedefrag 在线整理碎片 不用停机 增加 CPU
优化数据模型 从源头减少碎片 长期收益最好 需要改业务模型
MEMORY PURGE 归还可释放页 操作简单 效果不一定稳定
重启/切换 直接重建内存布局 效果最明显 有运维窗口要求
预留内存余量 抵抗 COW 和 RSS 峰值 风险低 可用容量下降

✅ 面试回答建议

Redis 的内存碎片化,指的是 Redis 逻辑上释放了内存,但操作系统看到的 RSS 仍然较高,或者分配器内部出现大量不连续空洞,导致内存利用率下降。常见原因包括频繁创建删除不同大小对象、大 key 反复修改、fork 期间的 COW 放大,以及内存分配器本身的行为。优化方法包括开启 activedefrag、优化 key/value 模型、控制大 key 和删除抖动、必要时执行 MEMORY PURGE,以及在运维窗口通过重启或主从切换重建内存布局。


🧾 七、Redis 的虚拟内存(VM)机制是什么?

这一题现在如果直接按"现有功能"回答,就容易答错。

1. 先说正确版本结论

  • Redis 曾经有过 VM(Virtual Memory)机制
  • 这个机制在 Redis 2.4 被标记为 deprecated
  • Redis 2.6 被移除

所以:

现代 Redis 面试里,VM 机制更多是一个历史知识点,而不是当前线上可用能力。

2. 它当年想解决什么问题?

它想解决的是:

内存不够,但又不想直接淘汰数据,能不能把冷数据先换到磁盘?

你可以把它理解成 Redis 早年的一种"值级别 swap"思路:

  • key 的元信息尽量还留在内存
  • value 比较大、又不常访问时
  • 就把 value 换出到磁盘文件
  • 真要访问这个 key,再从磁盘换回内存

3. 为什么后来被废弃?

看起来很美,但实践里问题很多:

(1)复杂度高

Redis 本来追求的是:

  • 内存访问简单直接
  • 单线程模型下低延迟
  • 代码路径尽量短

VM 引入后:

  • 要管理换入换出
  • 要维护页、swap 文件、线程/阻塞行为
  • 内部复杂度明显上升

(2)延迟不稳定

Redis 的价值之一是低延迟。

但一旦某次命中的是"被换出"的 value:

  • 就必须从磁盘读回来
  • 延迟波动会非常明显

这和 Redis 的设计目标其实是冲突的。

(3)收益不如预期

很多场景下,更简单有效的办法反而是:

  • 加内存
  • 做分片
  • 设置 TTL
  • 使用淘汰策略
  • 把 Redis 只当缓存,不当全量存储

所以 VM 机制最终没有成为 Redis 的主路线。

4. 今天该怎么理解这道题?

如果面试官问你"Redis 的 VM 是什么",推荐你这样答:

Redis 早期曾提供过 Virtual Memory 机制,目标是在内存不足时把冷 value 换出到磁盘,访问时再换回,以此节省 RAM。它本质上类似于 Redis 内部做 value 级别的 swap。但这个机制在 Redis 2.4 被废弃、2.6 被移除,因为它会显著增加系统复杂度,并带来不可控的磁盘 I/O 延迟,不符合 Redis 追求高性能、低延迟的设计目标。现代 Redis 更常用的做法是通过 TTL、淘汰策略、持久化、分片扩容等方式治理内存。


⚠️ 八、这些问题背后真正的工程风险是什么?

很多人把这几个知识点分开背,但线上问题往往是连在一起爆的。

典型事故链路

实例写入增长
内存接近上限
触发淘汰/命中率下降
业务重建缓存导致更多写入
恰好触发 BGSAVE/BGREWRITEAOF
fork + COW 放大
RSS 飙升
碎片严重 / OOM / RT 抖动

所以真正的核心挑战在于:

  • 你不能只会配置 maxmemory-policy
  • 还要知道持久化窗口、碎片、峰值内存、业务写流量之间会互相放大

✅ 更推荐的治理思路

  1. 先分清 Redis 的角色:到底是纯缓存,还是承担部分数据库职责。
  2. 给不同 key 设计清晰 TTL:不要让所有 key 无限期常驻。
  3. 合理设置 maxmemory 和淘汰策略:别等打满才想起来。
  4. 关注 fork 成本:内存越大、页表越大、快照越容易抖。
  5. 监控碎片和峰值内存 :不是只看 used_memory
  6. 避免大 key 和批量抖动删除:它们是碎片和延迟尖刺的常见源头。

📊 九、面试速查表

1. 六个问题的快速答案

问题 标准短答
Redis 的持久化机制有哪些? 主要是 RDB 和 AOF,生产常见是两者同时开启;纯缓存场景也可能关闭持久化。
Redis 在生成 RDB 文件时如何处理请求? 一般通过 BGSAVE,主进程 fork 子进程写 RDB,父进程继续处理请求;fork 有短暂阻塞,写入会触发 COW。
Redis 数据过期后的删除策略是什么? 惰性删除 + 定期删除。
Redis 中有哪些内存淘汰策略? 经典 8 种;如果按 Redis 8.6.0 官方,还新增 volatile-lrmallkeys-lrm
Redis 的内存碎片化是什么?如何优化? 逻辑内存下降但 RSS 仍高,通常由分配器、对象 churn、COW 等引起;可用 activedefrag、模型优化、MEMORY PURGE、重启切换等优化。
Redis 的 VM 机制是什么? 早期用于把冷 value 换出到磁盘的历史机制,2.4 废弃、2.6 移除。

2. 一段更像面试现场的话术

Redis 的内存治理可以分成三层:第一层是持久化,解决宕机恢复,主要是 RDB 和 AOF;第二层是过期删除和内存淘汰,解决内存满了怎么办,前者是惰性删除加定期删除,后者由 maxmemory-policy 决定删谁;第三层是内存碎片治理,解决 RSS 偏高、fork 风险大等问题,可以通过 activedefrag 和数据模型优化处理。至于 VM,是 Redis 早期把冷 value 换到磁盘的方案,但因为复杂度和延迟问题已经被废弃。


✅ 十、总结

这篇文章你真正要带走的,不是几个零散定义,而是下面这套理解框架:

  • RDB:快照,恢复快,但会丢最近窗口数据。
  • AOF:日志,更完整,但文件更大、恢复更慢。
  • RDB 生成期间 :主进程还能处理请求,但 fork 和 COW 是成本中心。
  • 过期删除:不是立刻删,而是惰性删除 + 定期删除。
  • 内存淘汰 :达到 maxmemory 后才触发,核心在于"删谁最不伤命中率"。
  • 内存碎片:不是数据变多,而是内存利用不整齐,RSS 会偏高。
  • VM:历史方案,今天知道原理和废弃原因即可。

如果你是拿这篇文章准备面试,我建议你重点练会下面三句话:

  1. RDB 快照期间,Redis 为什么还能继续处理请求?
  2. 过期删除和内存淘汰到底有什么区别?
  3. 为什么 Redis 不是只看 used_memory,还要看 RSS 和碎片率?

把这三句讲顺了,Redis 这组题基本就不是死记硬背,而是真的理解了。


🔗 参考资料

相关推荐
斌味代码2 小时前
Redis 分库分表实战:从垂直拆分到水平扩容完整记录
数据库·redis·bootstrap
Percep_gan2 小时前
在芋道自定义数据权限
java·数据库
哆啦阿梦2 小时前
Java AI 应用工程师 - 完整技能清单
java·开发语言·人工智能
rchmin2 小时前
阿里Tair分布式锁与Redis分布式锁的实现区别
数据库·redis·分布式
VT LI2 小时前
Lua 源码执行流程全解析:词法分析、语法分析、字节码生成、虚拟机执行与垃圾回收
java·开发语言·lua
凤山老林4 小时前
04-Java JDK, JRE和JVM
java·开发语言·jvm
camellias_10 小时前
【无标题】
java·tomcat
咸鱼2.010 小时前
【java入门到放弃】需要背诵
java·开发语言
椰猫子11 小时前
Java:异常(exception)
java·开发语言