1、AOF

1)概念

append-only-file只追加写,这个机制是对操作信息进行记录,其中记录的信息是以命令的形式存在,宕机之后恢复会根据AOF文件的命令进行执行恢复,这就是重放。AOF的过程是在主线程执行的,因此在Redis中是先执行命令再写入日志,这可以避免错误的命令被写入,也可以避免在执行命令之前对主线程的阻塞。

2)写回策略

AOF的记录实际上不是直接写文件的,因为这会导致IO异常高,因此每次执行命令后都是将命令序列化到内核为文件描述符提供的缓存中,之后根据策略进行文件写入。
写回策略有三种:
appendfsync always 代表每次执行命令后都会进行文件写入,会阻塞下一个命令执行,性能较低;
appendfsync no 代表由操作系统决定何时写回文件;
appendfsync everysec 代表每一秒进行aof文件写入,是对上面两种的折中。

3)AOF重写

当Redis运行久了,AOF就会变得很臃肿,这时候就需要进行瘦身操作,也即是AOF重写。AOF重写实际上是一个多变一的过程,假设在原来的AOF文件中记录的是对key的多次修改,那么实际上在当前我们只需要它的最后一次修改情况,因此AOF重写就可以将这多条命令转换为根据数据库现有状态重建的那一条命令,也就实现了瘦身。其原理就是fork出来一个新的进程,复制主进程的页表(虚实映射关系),进而在子进程中将内存中的数据转换为命令形式,开始执行AOF记录写入新的AOF文件,执行完毕用新的AOF文件替换旧的即可。
因为重写的过程主线程仍在运行,也就是还可能会有命令的执行发生,因此这时需要考虑同步的情况。在AOF重写过程中,如果有命令发生,会将命令同步记录到旧AOF缓存以及新AOF缓存中,这样可以很大程度上保证命令不丢失。

2、RDB

1)概念

将数据库中某时刻的键值对信息按照格式写入到rdb文件中,因此这里的RDB是一种快照机制,只是保存下执行SAVE时刻的数据库信息,在发生宕机之后会执行RDB恢复机制。其优点是恢复时直接读取文件数据即可,恢复速度比AOF快。需要注意的是RDB执行的是全量快照,即使改动的是一个key值,也是需要全量写入数据库信息,同时SAVE是会阻塞进程执行的,因此可以使用BGSAVE创建一个子进程来执行。

2)写时复制

这时候要考虑做快照时能不能修改数据的问题了,如果不允许修改数据,那么在快照做完之前都不能执行命令,显然是不可行的,因此需要考虑边持久化边处理操作,这时候采用的是操作系统提供的写时复制(COW)功能。
RDB在做快照的时候会fork出来一个bgsave子进程,与父进程共享内存数据,bgsave子进程会读取内存数据,写入RDB文件中。这时候如果对主线程中某块数据进行了修改,那么会将这块内存拷贝一个副本,之后bgsave子进程读取到副本中的内容,写入RDB文件中,此时主线程还是可以正常执行其他操作。

3)写入时机

既然是全量快照,那就不能每写入一个键值就开始,不然这开销大得离谱,一个是磁盘写入的压力,一个是fork进程的开销。那能不能一秒一次快照呢?也不能,这样的话出现宕机可能会丢失这一秒的快照信息,持久化得不到保障。可以考虑增量快照的方法,每次只快照修改的数据部分,但这仅凭RDB是做不到的,也就需要用到下面的混合持久化了。

3、混合持久化

上面提到了AOF可以记录增量的信息,RDB是全量的快照,那么如果我们需要增量的快照,实际上可以将这两个做一下融合。
RDB以一定的频率去执行,期间发生的增量数据用AOF来记录,这样除了可以避免频繁fork的开销,还可以避免AOF文件过大的情况。这种情况下RDB文件和AOF文件存在一起,这里的AOF只是增量的日志,而RDB依旧是全量的,Redis重启时会先从RDB恢复,之后再从AOF恢复。