Redis 支持 RDB 和 AOF 两种持久化机制,持久化功能有效地避免因进程 退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。
AOF 日志在长期的运行过程中会变的无比庞大,数据库重启时需要加载 AOF 日志进行指令重放,这个时间就会无比漫长。所以需要定期进行 AOF 重写,给 AOF 日志进行瘦身。
我们知道 Redis 是单线程程序,这个线程要同时负责多个客户端套接字的并发读写操作和内存数据结构的逻辑读写。
在服务线上请求的同时,Redis 还需要进行内存 RDB,内存 RDB 要求 Redis 必须进行文件 IO 操作,可文件 IO 操作是不能使用多路复用 API。这意味着单线程同时在服务线上的请求还要进行文件 IO 操作,文件 IO 操作会严重拖垮服务器请求的性能。还有个重要的问题是为了不阻塞线上的业务,就需要边持久化边响应客户端请求。持久化的同时,内存数据结构还在改变,比如一个大型的 hash 字典正在持久化,结果一个请求过来把它给删掉了,还没持久化完呢,这可怎么办?
那该怎么办呢? Redis 使用操作系统的多进程 COW(Copy On Write) 机制来实现 RDB 持久化,以下为 RDB 备份流程:

操作系统的COW(Copy On Write) 机制:
OS(操作系统) 领域 copy-on-write 核心思想则是 lazy copy。应用程序通常是不会直接和物理内存打交道的,所谓的内存寻址只是针对虚拟内存空间而言,而从虚拟内存到物理内存的映射则需要借助 MMU (存储管理单元)实现。
以 linux 为例,当通过系统调用(syscall)从一个已经存在的进程 P1 中 fork 出一个子进程 P2,OS会为 P2 创建一套与 P1 保持一致映射关系的虚拟内存空间,从而实现了 P1 和 P2 对物理空间的共享,这样做的目的是为了减少对物理内存的消耗,毕竟两份完全一样的数据没必要额外占用多一倍物理内存空间。此后,如果 P1 或 P2 需要更改某段内存,则须为其按需分配额外物理内存,将共享数据拷贝出来,供其修改,这里注意,无论父还是子进程,只要有修改,就会涉及到内存拷贝,这里的影响粒度范围是内存页,linux 内存页大小为 4KB。
通过OS copy-on-write 的过程我们可以总结出两个重要的特性:
基于这两个特性我们可以知道,copy-on-write 的在 OS 领域的设计初衷可能并非为了解决并发读的效率问题,参考维基[1]对 copy-on-write 的定义:
写入时复制(英语:Copy-on-write,简称COW)是一种计算机程序设计领域的优化策略。其核心思想是,如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(privatecopy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。
因为 OS 一开始并不需要给 fork 出来的新进程分配物理内存空间(同时大大减少了对物理内存的消耗,也提高了创建进程的效率),因此 copy-on-write 非常适合内存快照的 dump
AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的 指令记录。
假设 AOF 日志记录了自 Redis 实例创建以来所有的修改性指令序列,那么就可以通过 对一个空的 Redis 实例顺序执行所有的指令,也就是「重放」,来恢复 Redis 当前实例的内 存数据结构的状态。
Redis 会在收到客户端修改指令后,先进行参数校验,如果没问题,就立即将该指令文本存储到 AOF 日志中,也就是先存到磁盘,然后再执行指令。这样即使遇到突发宕机,已经存储到 AOF 日志的指令进行重放一下就可以恢复到宕机前的状态。通过 appendfsync 参数可以控制实时/秒级持久化 。
AOF 流程:

Redis 在长期运行的过程中,AOF 的日志会越变越长。如果实例宕机重启,重放整个 AOF 日志会非常耗时,导致长时间 Redis 无法对外提供服务。所以需要对 AOF 日志瘦身。
Redis 提供了 bgrewriteaof 指令用于对 AOF 日志进行瘦身。其原理就是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了。
AOF 瘦身重写流程:

AOF 重写可以通过 auto-aof-rewrite-min-siz e 和 auto-aof-rewrite- percentage 参数控制自动触发,也可以使用 bgrewriteaof 命令手动触发。
子进程执行期间使用 copy-on-write 机制与父进程共享内存,避免内 存消耗翻倍。AOF 重写期间还需要维护重写缓冲区,保存新的写入命令避免 数据丢失。
单机下部署多个实例时,为了防止出现多个子进程执行重写操作, 建议做隔离控制,避免 CPU 和 IO 资源竞争。
重启 Redis 时,我们很少使用 RDB 来恢复内存状态,因为会丢失大量数据。我们通常 使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实 例很大的情况下,启动需要花费很长的时间。混合持久化则综合了RDB和AOF两者的优势来进行持久化
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 RDB 文 件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自 持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。
于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可 以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
文章参考:https://mp.weixin.qq.com/s/-3fcK4WspGk6SEsaVrdx8A