Redis AOF重写机制为什么不会阻塞主线程
Redis 的 AOF(Append Only File)持久化机制通过记录每一次写命令来保证数据安全。随着运行时间的增加,AOF 文件体积会不断膨胀,这不仅占用磁盘空间,还会导致 Redis 重启时恢复数据变慢。为了解决这个问题,Redis 引入了 AOF 重写机制,将 AOF 文件压缩到最小。但重写需要遍历内存中庞大的数据,如果直接在主线程中执行,必然会导致服务阻塞。
Redis 采用“主线程 fork 子进程 + 写时复制 + AOF 重写缓冲区”的组合策略,完美解决了这一性能痛点。以下是该机制的底层原理与实现细节。
1. 执行 fork 操作,开辟独立进程
重写任务的第一步是创建子进程,而不是在主线程中直接执行。
- 调用 系统函数
fork()。 - 复制 主线程的进程页表。
- 生成 一个独立的子进程。
这一步是极快的,因为 fork 采用 Copy-On-Write(写时复制)技术。父子进程 initially 共享同一块物理内存。只有当内存数据被修改时,才会真正复制一份数据页。因此,在重写开始的那一刻,主线程几乎不会感觉到卡顿。
2. 子进程进行磁盘写入
子进程创建后,主线程继续响应用户请求,子进程则开始执行耗时较重的重写任务。
- 读取 当前内存中的数据库状态。
- 遍历 所有键值对。
- 生成 对应的写入命令(例如,如果一个键值为字符串 "hello",子进程会生成
SET key hello命令,而不是记录之前的多次修改历史)。 - 写入 到一个新的临时 AOF 文件中。
由于这个过程是在子进程中完成的,即使涉及大量的 CPU 计算和磁盘 I/O,也不会阻塞主线程处理客户端命令。
3. 主线程处理增量数据(AOF 重写缓冲区)
这是重写机制不丢失数据且不阻塞的关键所在。在子进程重写 AOF 文件的这段时间内,主线程依然在处理新的写请求。
为了平衡“旧文件”和“新文件”的差异,Redis 引入了 AOF 重写缓冲区。
主线程在处理新写命令时,需要同时做两件事:
- 将 命令追加到现有的
AOF 缓冲区(为了同步到旧的 AOF 文件,保证现有持久化不中断)。 - 将 命令追加到
AOF 重写缓冲区(这是专门为子进程重写准备的)。
这期间的数据流转如下:
4. 最终的合并与替换
当子进程完成重写任务后,它会向主线程发送一个信号。
- 接收 信号,主线程调用一个信号处理函数。
- 读取
AOF 重写缓冲区中在重写期间产生的所有增量写命令。 - 追加 这些增量命令到子进程生成的新 AOF 文件末尾。此时,新文件就包含了重写开始时刻的内存快照加上重写期间的所有新操作。
- 原子性地 使用
rename系统调用,用新文件覆盖旧的 AOF 文件。
在步骤 2 和 3 中,主线程是阻塞的,但由于 AOF 重写缓冲区 里的命令量通常不大(相比于全量数据),这个阻塞的时间极短(通常是毫秒级),对业务几乎无感知。
5. 配置建议
为了在性能和数据安全之间取得平衡,可以通过调整 redis.conf 中的参数来控制重写触发时机。
常用配置参数如下:
| 参数名 | 默认值 | 作用说明 |
|---|---|---|
auto-aof-rewrite-percentage |
100 | 当当前 AOF 文件大小是上次重写后大小的 100% 时触发。 |
auto-aof-rewrite-min-size |
64mb | 如果文件小于 64mb,即使达到比例也不重写,避免频繁重启。 |
手动触发重写:
如果需要立即重写,可以在客户端执行以下命令:
BGREWRITEAOF
该命令会指示 Redis 后台异步执行重写,效果与自动触发一致。

暂无评论,快来抢沙发吧!