不会丢失,因重写期间新命令同步写入独立的aof_rewrite_buf_blocks缓冲区,并通过管道实时推送至子进程;子进程完成快照后追加全部增量,最后原子替换文件,确保数据完整且不重复。

Redis AOF 重写过程中,新写入命令不会丢失,也不会重复,核心靠的是重写缓冲区(aof_rewrite_buf_blocks)与写时复制(Copy-on-Write)机制协同工作。它不是“合并”旧命令,而是暂存重写期间产生的增量变更,等子进程完成基础快照写入后,再把这部分增量追加进去,确保新AOF文件完整反映重写开始时刻的数据库状态 + 重写过程中的所有修改。
为什么需要重写缓冲区?
fork 子进程时,操作系统采用写时复制技术:子进程初始共享父进程内存页,但一旦父进程修改某块内存(比如执行 SET key val),内核会为父进程单独拷贝该页,子进程仍看到 fork 时刻的原始值。这意味着:
- 子进程只能基于 fork 那一瞬的内存快照生成新 AOF 文件;
- fork 后父进程继续处理客户端请求,这些新命令必须被记录下来,否则重写完成时就会丢数据;
- AOF 缓冲区(aof_buf)仍按原有策略(如 everysec)刷盘,服务于当前 AOF 文件,不能直接挪给重写流程用;
- 所以 Redis 单独开辟了 aof_rewrite_buf_blocks——一个链表结构的缓冲区,专用于暂存重写期间的所有写命令。
重写缓冲区怎么接收新写入?
每当父进程执行完一个写命令(如 INCR、RPUSH),在将命令追加到 aof_buf 的同时,也会以协议格式同步写入 aof_rewrite_buf_blocks 链表末尾。这个过程是原子且即时的,不依赖 flushAppendOnlyFile 调度。关键点:
- 每个缓冲块默认大小为 10MB(AOF_RW_BUF_BLOCK_SIZE = 10*1024*1024),满则自动分配新块;
- 命令以 Redis 通信协议格式(如 *3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n)写入,和 AOF 文件格式完全一致;
- 它只记录写命令,不记录读命令或过期键(因为过期键在重写时已被过滤);
- 即使重写耗时数秒,这块缓冲区也能持续承载高并发写入,只要内存够用。
子进程如何拿到并写入这些增量?
子进程不直接访问父进程内存,而是通过 Unix 域管道(pipe)接收父进程主动推送的数据:
- 父进程在 fork 后,会将 aof_rewrite_buf_blocks 中已有的内容,以及后续新增内容,分批次通过管道发送给子进程;
- 子进程从管道读取命令流,原样追加到正在构建的临时 AOF 文件末尾;
- 当子进程完成快照部分(如遍历 db、生成 SET/RPUSH 等精简命令)后,会继续读取管道直到 EOF,确保所有增量都被写入;
- 子进程退出前,临时文件已包含“全量快照 + 全部增量”,逻辑上等价于重写开始时刻起的最新状态。
最后一步:原子替换与数据一致性
子进程退出后,父进程做收尾操作:
- 检查子进程退出状态,确认重写成功;
- 将 aof_rewrite_buf_blocks 中可能尚未发完的最后一点数据(如有)也追加进临时文件;
- 调用 rename(2) 原子地将临时文件覆盖原 AOF 文件(如从 appendonly.aof.tmp → appendonly.aof);
- 整个过程无锁、无中断,客户端全程可读可写,新 AOF 文件上线即生效,不会出现命令缺失或重复。


















