AOF

RDB持久化是将进程数据写入文件,而AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中(有点像MySQL的binlog);当Redis重启时再次执行AOF文件中的命令来恢复数据。

与RDB相比,AOF的实时性更好,因此已成为主流的持久化方案。

AOF 持久化的实现

AOF 持久化功能的实现可以分为命令追加(append)、文件写入、文件同步(sync)三个步骤。

开启AOF

Redis服务器默认开启RDB,关闭AOF;要开启AOF,需要在配置文件中配置:

appendonly yes

命令追加

当 AOF 持久化功能处于打开状态时, 服务器在执行完一个写命令之后, 会以协议格式将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的末尾:

struct redisServer {

    // ...

    // AOF 缓冲区
    sds aof_buf;

    // ...
};

举个例子, 如果客户端向服务器发送以下命令:

redis> SET KEY VALUE
OK

那么服务器在执行这个 SET 命令之后, 会将以下协议内容追加到 aof_buf 缓冲区的末尾:

*3\r\n$3\r\nSET\r\n$3\r\nKEY\r\n$5\r\nVALUE\r\n

又比如说, 如果客户端向服务器发送以下命令:

redis> RPUSH NUMBERS ONE TWO THREE
(integer) 3

那么服务器在执行这个 RPUSH 命令之后, 会将以下协议内容追加到 aof_buf 缓冲区的末尾:

*5\r\n$5\r\nRPUSH\r\n$7\r\nNUMBERS\r\n$3\r\nONE\r\n$3\r\nTWO\r\n$5\r\nTHREE\r\n

以上就是 AOF 持久化的命令追加步骤的实现原理。

AOF 文件的写入与同步

Redis 的服务器进程就是一个事件循环(loop), 这个循环中的文件事件负责接收客户端的命令请求, 以及向客户端发送命令回复, 而时间事件则负责执行像 serverCron 函数这样需要定时运行的函数。

因为服务器在处理文件事件时可能会执行写命令, 使得一些内容被追加到 aof_buf 缓冲区里面, 所以在服务器每次结束一个事件循环之前, 它都会调用 flushAppendOnlyFile 函数, 考虑是否需要将 aof_buf 缓冲区中的内容写入和保存到 AOF 文件里面, 这个过程可以用以下伪代码表示:

def eventLoop():

    while True:

        # 处理文件事件,接收命令请求以及发送命令回复
        # 处理命令请求时可能会有新内容被追加到 aof_buf 缓冲区中
        processFileEvents()

        # 处理时间事件
        processTimeEvents()

        # 考虑是否要将 aof_buf 中的内容写入和保存到 AOF 文件里面
        flushAppendOnlyFile()

flushAppendOnlyFile 函数的行为由服务器配置的 appendfsync 选项的值来决定, 各个不同值产生的行为如表 TABLE_APPENDFSYNC 所示。

appendfsync 选项的值 flushAppendOnlyFile 函数的行为
always 将 aof_buf 缓冲区中的所有内容写入并同步到 AOF 文件。
everysec 将 aof_buf 缓冲区中的所有内容写入到 AOF 文件, 如果上次同步 AOF 文件的时间距离现在超过一秒钟, 那么再次对 AOF 文件进行同 步, 并且这个同步操作是由一个线程专门负责执行的。
no 将 aof_buf 缓冲区中的所有内容写入到 AOF 文件, 但并不对 AOF 文件进行同步, 何时同步由操作系统来决定。

如果用户没有主动为 appendfsync 选项设置值, 那么 appendfsync 选项的默认值为 everysec , 关于 appendfsync 选项的更多信息, 请参考 Redis 项目附带的示例配置文件 redis.conf 。

文件的写入和同步

为了提高文件的写入效率, 在现代操作系统中, 当用户调用 write 函数, 将一些数据写入到文件的时候, 操作系统通常会将写入数据暂时保存在一个内存缓冲区里面, 等到缓冲区的空间被填满、或者超过了指定的时限之后, 才真正地将缓冲区中的数据写入到磁盘里面。

这种做法虽然提高了效率, 但也为写入数据带来了安全问题, 因为如果计算机发生停机, 那么保存在内存缓冲区里面的写入数据将会丢失。

为此, 系统提供了 fsync 和 fdatasync 两个同步函数, 它们可以强制让操作系统立即将缓冲区中的数据写入到硬盘里面, 从而确保写入数据的安全性。

举个例子, 假设服务器在处理文件事件期间, 执行了以下三个写入命令:

SADD databases "Redis" "MongoDB" "MariaDB"
SET date "2013-9-5"
INCR click_counter 10086

那么 aof_buf 缓冲区将包含这三个命令的协议内容:

*5\r\n$4\r\nSADD\r\n$9\r\ndatabases\r\n$5\r\nRedis\r\n$7\r\nMongoDB\r\n$7\r\nMariaDB\r\n
*3\r\n$3\r\nSET\r\n$4\r\ndate\r\n$8\r\n2013-9-5\r\n
*3\r\n$4\r\nINCR\r\n$13\r\nclick_counter\r\n$5\r\n10086\r\n

如果这时 flushAppendOnlyFile 函数被调用, 假设服务器当前 appendfsync 选项的值为 everysec , 并且根据 server.aof_last_fsync 属性显示, 距离上次同步 AOF 文件已经超过一秒钟, 那么服务器会先将 aof_buf 中的内容写入到 AOF 文件中, 然后再对 AOF 文件进行同步。

以上就是对 AOF 持久化功能的文件写入和文件同步这两个步骤的介绍。

AOF 持久化的效率和安全性

服务器配置 appendfsync 选项的值直接决定 AOF 持久化功能的效率和安全性。

当 appendfsync 的值为 always 时, 服务器在每个事件循环都要将 aof_buf 缓冲区中的所有内容写入到 AOF 文件, 并且同步 AOF 文件, 所以 always 的效率是 appendfsync 选项三个值当中最慢的一个, 但从安全性来说, always 也是最安全的, 因为即使出现故障停机, AOF 持久化也只会丢失一个事件循环中所产生的命令数据。

当 appendfsync 的值为 everysec 时, 服务器在每个事件循环都要将 aof_buf 缓冲区中的所有内容写入到 AOF 文件, 并且每隔超过一秒就要在子线程中对 AOF 文件进行一次同步: 从效率上来讲, everysec 模式足够快, 并且就算出现故障停机, 数据库也只丢失一秒钟的命令数据。

当 appendfsync 的值为 no 时, 服务器在每个事件循环都要将 aof_buf 缓冲区中的所有内容写入到 AOF 文件, 至于何时对 AOF 文件进行同步, 则由操作系统控制。

因为处于 no 模式下的 flushAppendOnlyFile 调用无须执行同步操作, 所以该模式下的 AOF 文件写入速度总是最快的, 不过因为这种模式会在系统缓存中积累一段时间的写入数据, 所以该模式的单次同步时长通常是三种模式中时间最长的: 从平摊操作的角度来看, no 模式和 everysec 模式的效率类似, 当出现故障停机时, 使用 no 模式的服务器将丢失上次同步 AOF 文件之后的所有写命令数据。

文件重写(rewrite)

随着时间流逝,Redis服务器执行的写命令越来越多,AOF文件也会越来越大;过大的AOF文件不仅会影响服务器的正常运行,也会导致数据恢复需要的时间过长。

文件重写是指定期重写AOF文件,减小AOF文件的体积。

注意

  1. AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操作!

  2. 对于AOF持久化来说,文件重写虽然是强烈推荐的,但并不是必须的;即使没有文件重写,数据也可以被持久化并在Redis启动的时候导入;因此在一些实现中,会关闭自动的文件重写,然后通过定时任务在每天的某一时刻定时执行。

为什么可以压缩

文件重写之所以能够压缩AOF文件,原因在于:

  1. 过期的数据不再写入文件

  2. 无效的命令不再写入文件:如有些数据被重复设值(set mykey v1, set mykey v2)、有些数据被删除了(sadd myset v1, del myset)等等

  3. 多条命令可以合并为一个:如sadd myset v1, sadd myset v2, sadd myset v3可以合并为sadd myset v1 v2 v3。

不过为了防止单条命令过大造成客户端缓冲区溢出,对于list、set、hash、zset类型的key,并不一定只使用一条命令;而是以某个常量为界将命令拆分为多条。

这个常量在 redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD 中定义,不可更改,3.0版本中值是64。

文件重写的触发

文件重写的触发,分为手动触发和自动触发:

手动触发

直接调用bgrewriteaof命令,该命令的执行与bgsave有些类似:都是fork子进程进行具体的工作,且都只有在fork时阻塞。

自动触发

自动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数,以及 aof_current_size 和 aof_base_size 状态确定触发时机。

auto-aof-rewrite-min-size:执行AOF重写时,文件的最小体积,默认值为64MB。

auto-aof-rewrite-percentage:执行AOF重写时,当前AOF大小(即aof_current_size)和上一次重写时AOF大小(aof_base_size)的比值。

只有当auto-aof-rewrite-min-size和auto-aof-rewrite-percentage两个参数同时满足时,才会自动触发AOF重写,即bgrewriteaof操作。

auto-bgrewriteaof

关于文件重写的流程,有两点需要特别注意:

  1. 重写由父进程fork子进程进行;

  2. 重写期间Redis执行的写命令,需要追加到新的AOF文件中,为此Redis引入了aof_rewrite_buf缓存。

整体流程

对照上图,文件重写的流程如下:

1) Redis父进程首先判断当前是否存在正在执行 bgsave/bgrewriteaof的子进程,如果存在则bgrewriteaof命令直接返回,如果存在bgsave命令则等bgsave执行完成后再执行。前面曾介绍过,这个主要是基于性能方面的考虑。

2) 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的。

3.1) 父进程fork后,bgrewriteaof命令返回”Background append only file rewrite started”信息并不再阻塞父进程,并可以响应其他命令。Redis的所有写命令依然写入AOF缓冲区,并根据appendfsync策略同步到硬盘,保证原有AOF机制的正确。

3.2) 由于fork操作使用写时复制技术,子进程只能共享fork操作时的内存数据。由于父进程依然在响应命令,因此Redis使用AOF重写缓冲区(图中的aof_rewrite_buf)保存这部分数据,防止新AOF文件生成期间丢失这部分数据。也就是说,bgrewriteaof执行期间,Redis的写命令同时追加到aof_buf和aof_rewirte_buf两个缓冲区。

4) 子进程根据内存快照,按照命令合并规则写入到新的AOF文件。

5.1) 子进程写完新的AOF文件后,向父进程发信号,父进程更新统计信息,具体可以通过info persistence查看。

5.2) 父进程把AOF重写缓冲区的数据写入到新的AOF文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致。

5.3) 使用新的AOF文件替换老文件,完成AOF重写。

状态查看

其中,参数可以通过config get命令查看:


状态可以通过info persistence查看:


启动时加载

前面提到过,当AOF开启时,Redis启动时会优先载入AOF文件来恢复数据;只有当AOF关闭时,才会载入RDB文件恢复数据。

Redis 启动的时候,会提示从什么文件加载数据。

文件校验

与载入RDB文件类似,Redis载入AOF文件时,会对AOF文件进行校验,如果文件损坏,则日志中会打印错误,Redis启动失败。

但如果是AOF文件结尾不完整(机器突然宕机等容易导致文件尾部不完整),且aof-load-truncated参数开启,则日志中会输出警告,Redis忽略掉AOF文件的尾部,启动成功。

aof-load-truncated参数默认是开启的:

错误如何修复

服务器可能在程序正在对AOF文件进行写入时停机,如果停机造成了 AOF 文件出错(corrupt),那么Redis在重启时会拒绝载入这个AOF文件, 从而确保数据的一致性不会被破坏。当发生这种情况时,可以用以下方法来修复出错的 AOF 文件:为现有的AOF文件创建一个备份。然后使用Redis附带的redis-check-aof –fix程序对原来的AOF文件进行修复。

然后可选使用 diff -u 对比修复后的 AOF 文件和原始 AOF 文件的备份,查看两个文件之间的不同之处。再次重启Redis服务器,等待服务器载入修复后的AOF文件,并进行数据恢复。

但如果是AOF文件结尾不完整(机器突然宕机等容易导致文件尾部不完整),且aof-load-truncated参数开启,则日志中会输出警告,Redis忽略掉AOF文件的尾部,启动成功。aof-load-truncated参数默认是开启的。

伪客户端

因为Redis的命令只能在客户端上下文中执行,而载入AOF文件时命令是直接从文件中读取的,并不是由客户端发送;因此Redis服务器在载入AOF文件之前,会创建一个没有网络连接的客户端,之后用它来执行AOF文件中的命令,命令执行的效果与带网络连接的客户端完全一样。

AOF 常用配置总结

下面是 AOF 常用的配置项,以及默认值;前面介绍过的这里不再详细介绍。

appendonly no:是否开启AOF 
appendfilename “appendonly.aof”:AOF文件名 
dir ./:RDB文件和AOF文件所在目录 
appendfsync everysec:fsync持久化策略 
no-appendfsync-on-rewrite no:AOF重写期间是否禁止fsync;如果开启该选项,可以减轻文件重写时CPU和硬盘的负载(尤其是硬盘),但是可能会丢失AOF重写期间的数据;需要在负载和安全性之间进行平衡 
auto-aof-rewrite-percentage 100:文件重写触发条件之一 
auto-aof-rewrite-min-size 64mb:文件重写触发提交之一 
aof-load-truncated yes:如果AOF文件结尾损坏,Redis启动时是否仍载入AOF文件

方案选择与常见问题(重点)

在介绍持久化策略之前,首先要明白无论是RDB还是AOF,持久化的开启都是要付出性能方面代价的:

RDB

对于RDB持久化,一方面是bgsave在进行fork操作时Redis主进程会阻塞,

另一方面,子进程向硬盘写数据也会带来IO压力;

AOF

对于AOF持久化,向硬盘写数据的频率大大提高(everysec策略下为秒级),IO压力更大,甚至可能造成AOF追加阻塞问题(后面会详细介绍这种阻塞),

此外,AOF文件的重写与RDB的bgsave类似,会有fork时的阻塞和子进程的IO压力问题。

相对来说,由于AOF向硬盘中写数据的频率更高,因此对Redis主进程性能的影响会更大。

因地制宜

实际生产环境中,根据数据量、应用对数据的安全要求、预算限制等不同情况,会有各种各样的持久化策略;如完全不使用任何持久化、使用RDB或AOF的一种,或同时开启RDB和AOF持久化等。

此外,持久化的选择必须与Redis的主从策略一起考虑,因为主从复制与持久化同样具有数据备份的功能,而且主机master和从机slave可以独立的选择持久化方案。

下面分场景来讨论持久化策略的选择,下面的讨论也只是作为参考,实际方案可能更复杂更具多样性。

(1)如果Redis中的数据完全丢弃也没有关系(如Redis完全用作DB层数据的cache),那么无论是单机,还是主从架构,都可以不进行任何持久化。

(2)在单机环境下(对于个人开发者,这种情况可能比较常见),如果可以接受十几分钟或更多的数据丢失,选择RDB对Redis的性能更加有利;如果只能接受秒级别的数据丢失,应该选择AOF。

(3)但在多数情况下,我们都会配置主从环境,slave的存在既可以实现数据的热备,也可以进行读写分离分担Redis读请求,以及在master宕掉后继续提供服务。

在这种情况下,一种可行的做法是:

master:完全关闭持久化(包括RDB和AOF),这样可以让master的性能达到最好

slave:关闭RDB,开启AOF(如果对数据安全要求不高,开启RDB关闭AOF也可以),并定时对持久化文件进行备份(如备份到其他文件夹,并标记好备份的时间);然后关闭AOF的自动重写,然后添加定时任务,在每天Redis闲时(如凌晨12点)调用bgrewriteaof。

这里需要解释一下,为什么开启了主从复制,可以实现数据的热备份,还需要设置持久化呢?

因为在一些特殊情况下,主从复制仍然不足以保证数据的安全,例如:

master和slave进程同时停止:考虑这样一种场景,如果master和slave在同一栋大楼或同一个机房,则一次停电事故就可能导致master和slave机器同时关机,Redis进程停止;如果没有持久化,则面临的是数据的完全丢失。

master误重启:考虑这样一种场景,master服务因为故障宕掉了,如果系统中有自动拉起机制(即检测到服务停止后重启该服务)将master自动重启,由于没有持久化文件,那么master重启后数据是空的,slave同步数据也变成了空的;如果master和slave都没有持久化,同样会面临数据的完全丢失。

需要注意的是,即便是使用了哨兵(关于哨兵后面会有文章介绍)进行自动的主从切换,也有可能在哨兵轮询到master之前,便被自动拉起机制重启了。因此,应尽量避免“自动拉起机制”和“不做持久化”同时出现。

(4)异地灾备:上述讨论的几种持久化策略,针对的都是一般的系统故障,如进程异常退出、宕机、断电等,这些故障不会损坏硬盘。

但是对于一些可能导致硬盘损坏的灾难情况,如火灾地震,就需要进行异地灾备。

例如对于单机的情形,可以定时将RDB文件或重写后的AOF文件,通过scp拷贝到远程机器,如阿里云、AWS等;对于主从的情形,可以定时在master上执行bgsave,然后将RDB文件拷贝到远程机器,或者在slave上执行bgrewriteaof重写AOF文件后,将AOF文件拷贝到远程机器上。

一般来说,由于RDB文件文件小、恢复快,因此灾难恢复常用RDB文件;异地备份的频率根据数据安全性的需要及其他条件来确定,但最好不要低于一天一次。

fork阻塞:CPU的阻塞

在Redis的实践中,众多因素限制了Redis单机的内存不能过大。

例如:

当面对请求的暴增,需要从库扩容时,Redis内存过大会导致扩容时间太长;

当主机宕机时,切换主机后需要挂载从库,Redis内存过大导致挂载速度过慢;

以及持久化过程中的fork操作,下面详细说明。

首先说明一下fork操作:

父进程通过fork操作可以创建子进程;子进程创建后,父子进程共享代码段,不共享进程的数据空间,但是子进程会获得父进程的数据空间的副本。

在操作系统fork的实际实现中,基本都采用了写时复制技术,即在父/子进程试图修改数据空间之前,父子进程实际上共享数据空间;但是当父/子进程的任何一个试图修改数据空间时,操作系统会为修改的那一部分(内存的一页)制作一个副本。

虽然fork时,子进程不会复制父进程的数据空间,但是会复制内存页表(页表相当于内存的索引、目录);父进程的数据空间越大,内存页表越大,fork时复制耗时也会越多。

在Redis中,无论是RDB持久化的bgsave,还是AOF重写的bgrewriteaof,都需要fork出子进程来进行操作。

如果Redis内存过大,会导致fork操作时复制内存页表耗时过多;而Redis主进程在进行fork时,是完全阻塞的,也就意味着无法响应客户端的请求,会造成请求延迟过大。

对于不同的硬件、不同的操作系统,fork操作的耗时会有所差别,一般来说,如果Redis单机内存达到了10GB,fork时耗时可能会达到百毫秒级别(如果使用Xen虚拟机,这个耗时可能达到秒级别)。因此,一般来说Redis单机内存一般要限制在10GB以内;不过这个数据并不是绝对的,可以通过观察线上环境fork的耗时来进行调整。观察的方法如下:执行命令info stats,查看latest_fork_usec的值,单位为微秒。

为了减轻fork操作带来的阻塞问题,除了控制Redis单机内存的大小以外,还可以适度放宽AOF重写的触发条件、选用物理机或高效支持fork操作的虚拟化技术等,例如使用Vmware或KVM虚拟机,不要使用Xen虚拟机。

AOF追加阻塞:硬盘的阻塞

前面提到过,在AOF中,如果AOF缓冲区的文件同步策略为everysec,则:在主线程中,命令写入aof_buf后调用系统write操作,write完成后主线程返回;fsync同步文件操作由专门的文件同步线程每秒调用一次。

这种做法的问题在于,如果硬盘负载过高,那么fsync操作可能会超过1s;如果Redis主线程持续高速向aof_buf写入命令,硬盘的负载可能会越来越大,IO资源消耗更快;如果此时Redis进程异常退出,丢失的数据也会越来越多,可能远超过1s。

为此,Redis的处理策略是这样的:主线程每次进行AOF会对比上次fsync成功的时间;如果距上次不到2s,主线程直接返回;如果超过2s,则主线程阻塞直到fsync同步完成。

因此,如果系统硬盘负载过大导致fsync速度太慢,会导致Redis主线程的阻塞;此外,使用everysec配置,AOF最多可能丢失2s的数据,而不是1s。

定位

AOF追加阻塞问题定位的方法:

(1)监控info Persistence中的aof_delayed_fsync:当AOF追加阻塞发生时(即主线程等待fsync而阻塞),该指标累加。

(2)AOF阻塞时的Redis日志:

Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis. 

(3)如果AOF追加阻塞频繁发生,说明系统的硬盘负载太大;可以考虑更换IO速度更快的硬盘,或者通过IO监控分析工具对系统的IO负载进行分析,如iostat(系统级io)、iotop(io版的top)、pidstat等。

总结

RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储。

AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾,redis还能对AOF文件进行后台重写,使得AOF文件的体积还不至于过大。

只做缓存:如果你希望你的数据在服务器运行的时候存在,你也可以不使用任何形式的持久化方式。

同时开启

同时开启两种持久化方式:

  1. 在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。

  2. RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要使用AOF呢?

作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。

RDB 与 AOF 的对比

RDB 的优缺点

RDB 的优点

RDB 是一个比较紧凑的文件,它保存了 Redis 在某个时间点的数据,这种数据比较适合做备份和用于灾难恢复。

比如说,你可以在最近的 24 小时内,每小时备份一次 RDB 文件,并且在每个月的每一天,也备份一个 RDB 文件。

这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本。

RDB 的缺点

如果你需要尽量避免在服务器故障时丢失数据,那么 RDB 不适合你。

虽然 Redis 允许你设置不同的保存点来控制保存 RDB 文件的频率, 但是, 因为 RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。

因此你可能会至少 5 分钟才保存一次 RDB 文件。

在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。

AOF 的优缺点

AOF 的优点

1,使用 AOF 做持久化,可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。

AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据。

fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求。

2,AOF 文件是一个只进行追加操作的日志文件,不是生成新的之后替换掉那种,即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也可以轻易地修复这种问题。

3,Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。

整个重写操作是绝对安全的,因为 Redis 重写是创建新 AOF 文件,重写的过程中会继续将命令追加到现有旧的 AOF 文件里面,即使重写过程中发生停机,现有旧的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。

4,AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSH ALL(清空整个 Redis 服务器的数据(删除所有数据库的所有 key )。) 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

AOF 的缺点

对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。

根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。

不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。

RDB 和 AOF 二者的区别

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式追加记录,可以打开文件看到详细的操作记录。

RDB 和 AOF 我应该用哪一个?

如果你非常关心你的数据,但仍然可以承受数分钟以内的数据丢失,那么你可以只使用 RDB 持久。

AOF 将 Redis 执行的每一条命令追加到磁盘中,处理巨大的写入会降低 Redis 的性能,不知道你是否可以接受。

数据库备份和灾难恢复:

定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。

Redis 支持同时开启 RDB 和 AOF,系统重启后,Redis 会优先使用 AOF 来恢复数据,这样丢失的数据会最少。

备份 Redis 数据

磁盘故障, 节点失效, 诸如此类的问题都可能让你的数据消失不见,不进行备份是非常危险的。

Redis 对于数据备份是非常友好的, 因为你可以在服务器运行的时候对 RDB 文件进行复制:

RDB 文件一旦被创建, 就不会进行任何修改。

当服务器要创建一个新的 RDB 文件时, 它先将文件的内容保存在一个临时文件里面, 当临时文件写入完毕时, 程序才使用 rename(2) 原子地用临时文件替换原来的 RDB 文件。

这也就是说, 无论何时, 复制 RDB 文件都是绝对安全的。

建议

以下是我们的建议:

1,创建一个定期任务(cron job), 每小时将一个 RDB 文件备份到一个文件夹, 并且每天将一个 RDB 文件备份到另一个文件夹。

2,确保快照的备份都带有相应的日期和时间信息, 每次执行定期任务脚本时, 使用 find 命令来删除过期的快照: 比如说, 你可以保留最近 48 小时内的每小时快照, 还可以保留最近一两个月的每日快照。

3,至少每天一次, 将 RDB 备份到你的数据中心之外, 或者至少是备份到你运行 Redis 服务器的物理机器之外。

个人收获

buffer

使用缓存提升写的效率,是一个非常好的思想。

类似于 log 也有类似的功能,缺点就是可能会缺失数据。

参考资料

AOF 持久化的实现

RDB 和 AOF 持久化的原理是什么?我应该用哪一个?它们的优缺点?