# Redis 在生成 RDB 文件时如何处理请求?
6 min read
Table of Contents
Redis 在生成 RDB 文件时如何处理请求?
【核心定义】
Redis 生成 RDB 文件时,默认采用 写时复制(Copy-on-Write, COW) 机制,在保证数据一致性的同时,允许主线程继续处理客户端请求。
【关键要点】
- 子进程负责持久化:主进程 fork 出一个子进程,子进程拥有与父进程相同的内存数据副本,专门负责将数据写入 RDB 文件,而父进程继续处理命令。
- 写时复制优化内存:fork 操作后,父子进程共享同一物理内存;当父进程修改某块数据时,操作系统会将该内存页复制一份,确保子进程看到的是 fork 瞬间的数据快照。
- RDB 生成期间数据可修改:客户端写入请求会由主进程正常处理,修改后的数据对子进程不可见,因此 RDB 文件保存的是 fork 时刻的数据一致性视图。
- 无锁设计保证性能:由于子进程只读数据,无需加锁,避免了持久化阻塞服务,但大量写入会导致内存复制开销增加。
【深度推导/细节】
核心矛盾:如何在持久化过程中平衡数据一致性与服务可用性。
- Step 1 – fork 瞬间冻结视图:fork 调用后,子进程获得父进程内存空间的指针映射,此时内存数据被固定为 RDB 的“逻辑快照”。
- Step 2 – 写入触发 COW:若主进程接收到
SET key new_value命令,需修改某键值对所在内存页,操作系统会先复制该页供父进程修改,子进程仍读取原页内容。 - Step 3 – 内存开销临界点:如果持久化期间所有内存页都被修改,则父进程内存占用会接近 2 倍原始数据量,这是 RDB 持久化时内存暴增的根本原因。
- Step 4 – RDB 文件生成结束:子进程将全部数据写入临时文件,完成后替换旧 RDB 文件,最后子进程退出,释放复制的内存页。
关键数字解析:
- 无 8/64 等阈值:与 HashMap 不同,RDB 过程没有类似的桶转换阈值,其性能瓶颈主要取决于 写操作比例 和 内存页大小(通常 4KB)。
- fork 延迟敏感:当 Redis 内存达 10GB 时,fork 可能阻塞主线程数十毫秒至数秒(取决于系统内存分配策略),影响短暂的服务响应。
【关联/对比】
- vs AOF 持久化:AOF 采用追加日志模式,持久化期间通过
aof_rewrite同样使用子进程 + COW,但 AOF 日常写入是顺序追加,而 RDB 是全量快照。 - vs 混合持久化:Redis 4.0 后支持 RDB + AOF 混合模式,RDB 做全量基础,AOF 记录增量,兼顾恢复速度与数据安全性。
- vs 内存数据库持久化通病:类似 MongoDB 的 WiredTiger 引擎也采用 COW 机制,但通过检查点(checkpoint)实现,差异在于磁盘布局与并发控制策略。
『面试官追问』
- RDB 持久化期间服务完全无阻塞吗?
- fork 阶段会阻塞主线程,尤其在大内存实例中。Redis 6.0 引入
io-threads仅优化网络 I/O,不解决 fork 阻塞。
- fork 阶段会阻塞主线程,尤其在大内存实例中。Redis 6.0 引入
- 如果持久化过程中 Redis 崩溃,数据会丢失多少?
- 丢失最后一次 RDB 保存后的所有修改,取决于
save配置的时间间隔(例如 900 秒内 1 个键变化则触发)。
- 丢失最后一次 RDB 保存后的所有修改,取决于
- 如何减少 COW 导致的内存翻倍风险?
- 控制实例内存大小,或采用 AOF 的
appendfsync everysec,降低全量快照频率;在低峰期手动执行BGSAVE。
- 控制实例内存大小,或采用 AOF 的
- RDB 文件格式是怎样的?
- 二进制格式,包含魔数、版本、数据库编号、键值对(带过期时间戳)及结束符,采用长度编码压缩存储。
【线程安全与版本差异】
- 线程安全:RDB 生成全程依赖操作系统的 COW 机制,无需应用层锁,但父进程的写入会触发内存复制,本质上是通过内核级页面保护实现的“隐式同步”。
- 版本演进:
- Redis 2.4 前:
SAVE命令同步阻塞主线程,已弃用。 - Redis 2.8+:
BGSAVE成为主流,支持后台执行。 - Redis 4.0:引入
RDB-AOF混合持久化,RDB 文件可作为 AOF 重写的基础。 - Redis 7.0:优化 RDB 文件加载速度,支持多线程加载(仅限企业版)。
- Redis 2.4 前:
总结:RDB 持久化通过 COW 机制在数据一致性与服务可用性间取得平衡,适用于容灾备份与全量恢复场景,但需警惕 fork 延迟与内存翻倍对高性能服务的影响。