[TOC] ## 一、RDB持久化 对redis中的数据执行周期性的持久化,通过配置文件中设置检查间隔时间与备份触发条件来对数据进行周期性的持久化。当redis重启时,并且AOF持久化未开启时,redis会读取RDB持久化生成的二进制文件(默认名称dump.rdb,可通过设置dbfilename修改)进行数据恢复,对于持久化信息可以用过命令“info Persistence”查看。 优点: * RDB会生成多个数据文件,每个数据文件都代表了某一个时刻中redis的数据,这种多个数据文件的方式,非常适合做冷备份。 * RDB对redis对外提供的读写服务,影响非常小,可以让redis保持高性能,因为redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可。 * 相对于AOF持久化机制来说,直接基于RDB数据文件来重启和恢复redis进程,更加快速。 缺点: * 如果想要在redis故障时,尽可能少的丢失数据,那么RDB没有AOF好。一般来说,RDB数据快照文件,都是每隔5分钟,或者更长时间生成一次,这个时候就得接受一旦redis进程宕机,那么会丢失最近5分钟的数据。这个问题,也是rdb最大的缺点,就是不适合做第一优先的恢复方案,如果你依赖RDB做第一优先恢复方案,会导致数据丢失的比较多。 * RDB每次在fork子进程来执行RDB快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒一般不要让RDB的间隔太长,否则每次生成的RDB文件太大了,对redis本身的性能可能会有影响的。 * RDB文件是特定的格式,阅读性差,由于格式固定,可能存在不兼容情况。 ### 1\. 快照触发条件 RDB生成快照可自动促发,也可以使用命令手动触发,以下是redis触发执行快照条件,后续会对每个条件详细说明: 1. 客户端执行命令save和bgsave会生成快照; 2. 根据配置文件save m n规则进行自动快照; 3. 主从复制时,从库全量复制同步主库数据,此时主库会执行bgsave命令进行快照; 4. 客户端执行数据库清空命令FLUSHALL时候,触发快照; 5. 客户端执行shutdown关闭redis时,触发快照; ### 2\. RDB持久化配置 ~~~ #配置快照(rdb)促发规则,格式:save <seconds> <changes> save 900 1 #900秒内至少有1个key被改变则做一次快照 save 300 10 #300秒内至少有300个key被改变则做一次快照 save 60 10000 #60秒内至少有10000个key被改变则做一次快照 save "" #关闭该规则 #rdb持久化存储数据库文件名,默认为dump.rdb dbfilename dump.rdb #yes代表当使用bgsave命令持久化出错时候停止写RDB快照文件,no表明忽略错误继续写文件。 stop-write-on-bgsave-error yes #在写入文件和读取文件时是否开启rdb文件检查,检查是否有无损坏,如果在启动是检查发现损坏,则停止启动。 rdbchecksum yes #数据文件存放目录,rdb快照文件和aof文件都会存放至该目录,请确保有写权限 dir "/etc/redis" #是否开启RDB文件压缩,该功能可以节约磁盘空间 rdbcompression yes ~~~ ## 二、AOF持久化 当redis存储非临时数据时,为了降低redis故障而引起的数据丢失,redis提供了AOF(Append Only File)持久化,从单词意思讲,将命令追加到文件。AOF可以将Redis执行的每一条写命令追加到磁盘文件(appendonly.aof)中,在redis启动时候优先选择从AOF文件恢复数据。由于每一次的写操作,redis都会记录到文件中,所以开启AOF持久化会对性能有一定的影响,但是大部分情况下这个影响是可以接受的,我们可以使用读写速率高的硬盘提高AOF性能。与RDB持久化相比,AOF持久化数据丢失更少,其消耗内存更少(RDB方式执行bgsve会有内存拷贝)。 在生产环境里面,一般来说AOF都是要打开的,除非你说随便丢个几分钟的数据也无所谓。 优点: * AOF可以更好的保护数据不丢失,每来一条数据,会写入os cache,然后linux会每隔1秒,通过一个后台线程执行一次fsync操作(fsync的功能是确保所有已修改的内容已经正确同步到硬盘上,该调用会阻塞等待直到设备报告IO完成。),最多丢失1秒钟的数据(机器宕机,如果只是redis奔溃,数据也不会丢失)。 * AOF日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复。 * AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在rewritelog的时候,会对其中的指导进行压缩,创建出一份需要恢复数据的最小日志出来。再创建新日志文件的时候,老的日志文件还是照常写入。当新的merge后的日志文件ready的时候,再交换新老日志文件即可。 * AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据。 缺点: * 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大。 * AOF开启后,支持的写QPS会比RDB支持的写QPS低,因为AOF一般会配置成每秒fsync一次日志文件。尽管每秒一次fsync,性能也还是很高的,如果你要保证一条数据都不丢,也是可以的,AOF的fsync设置成没写入一条数据,fsync一次,那就完蛋了,redis的QPS将会更低。 * 以前AOF发生过bug,就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。所以说,类似AOF这种较为复杂的基于命令日志/merge/回放的方式,比基于RDB每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有bug。不过AOF就是为了避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。 * 唯一的比较大的缺点,其实就是做数据恢复的时候,会比较慢,还有做冷备,定期的备份,不太方便,可能要自己手写复杂的脚本去做,做冷备不太合适。RDB恢复日志,就是一份数据文件,恢复的时候,直接加载到内存中即可。而AOF则不同,做数据恢复的时候,其实是要回放和执行所有的指令日志,来恢复出来内存中的所有数据的。 ### 1\. 开启AOF 默认情况下,redis是关闭了AOF持久化,开启AOF通过配置appendonly为yes开启 ### 2\. AOF持久化过程 redisAOF持久化过程可分为以下阶段: #### 2.1. 追加写入 redis将每一条写命令以redis通讯协议添加至缓冲区aof\_buf,这样的好处在于在大量写请求情况下,采用缓冲区暂存一部分命令随后根据策略一次性写入磁盘,这样可以减少磁盘的I/O次数,提高性能。 #### 2.2. 同步命令到硬盘 当写命令写入aof\_buf缓冲区后,redis会将缓冲区的命令写入到文件,redis提供了三种同步策略,由配置参数appendfsync决定,下面是每个策略所对应的含义: * no:不使用fsync方法同步,而是交给操作系统write函数去执行同步操作,在linux操作系统中大约每30秒刷一次缓冲。这种情况下,缓冲区数据同步不可控,并且在大量的写操作下,aof\_buf缓冲区会堆积会越来越严重,一旦redis出现故障,数据丢失严重。 * always:表示每次有写操作都调用fsync方法强制内核将数据写入到aof文件。这种情况下由于每次写命令都写到了文件中, 虽然数据比较安全,但是因为每次写操作都会同步到AOF文件中,所以在性能上会有影响,同时由于频繁的IO操作,硬盘的使用寿命会降低。 * everysec:数据将使用调用操作系统write写入文件,并使用fsync每秒一次从内核刷新到磁盘。 这是折中的方案,兼顾性能和数据安全,所以redis默认推荐使用该配置。 #### 2.3. 文件重写(bgrewriteaof) 当开启的AOF时,随着时间推移,AOF文件会越来越大,当然redis也对AOF文件进行了优化,即触发AOF文件重写条件(后续会说明)时候,redis将使用bgrewriteaof对AOF文件进行重写。这样的好处在于减少AOF文件大小,同时有利于数据的恢复。   为什么重写?比如先后执行了“set foo bar1 set foo bar2 set foo bar3” 此时AOF文件会记录三条命令,这显然不合理,因为文件中应只保留“set foo bar3”这个最后设置的值,前面的set命令都是多余的,下面是一些重写时候策略: * 重复或无效的命令不写入文件 * 过期的数据不再写入文件 * 多条命令合并写入(当多个命令能合并一条命令时候会对其优化合并作为一个命令写入,例如“RPUSH list1 a RPUSH list1 b" 合并为“RPUSH list1 a b” ) ### 3\. AOF实现本质 AOF实现本质是基于redis通讯协议,将命令以纯文本的方式写入到文件中。 redis协议: 首先Redis是以行来划分,每行以\\r\\n行结束。每一行都有一个消息头,消息头共分为5种分别如下: (+) 表示一个正确的状态信息,具体信息是当前行+后面的字符。 (-)  表示一个错误信息,具体信息是当前行-后面的字符。 (\*) 表示消息体总共有多少行,不包括当前行,\*后面是具体的行数。 ($) 表示下一行数据长度,不包括换行符长度\\r\\n,$后面则是对应的长度的数据。 (:) 表示返回一个数值,:后面是相应的数字节符。 ### 4\. AOF配置参数 ~~~ #AOF文件最小重写大小,只有当AOF文件大小大于该值时候才可能重写,4.0默认配置64mb。 auto-aof-rewrite-min-size 64mb #当前AOF文件大小和最后一次重写后的大小之间的比率等于或者等于指定的增长百分比,如100代表当前AOF文件是上次重写的两倍时候才重写。 auto-aof-rewrite-percentage 100 #no:不使用fsync方法同步,而是交给操作系统write函数去执行同步操作,在linux操作系统中大约每30秒刷一次缓冲。这种情况下,缓冲区数据同步不可控,并且在大量的写操作下,aof_buf缓冲区会堆积会越来越严重,一旦redis出现故障,数据 #always:表示每次有写操作都调用fsync方法强制内核将数据写入到aof文件。这种情况下由于每次写命令都写到了文件中, 虽然数据比较安全,但是因为每次写操作都会同步到AOF文件中,所以在性能上会有影响,同时由于频繁的IO操作,硬盘的使用寿命会降低。 #everysec:数据将使用调用操作系统write写入文件,并使用fsync每秒一次从内核刷新到磁盘。 这是折中的方案,兼顾性能和数据安全,所以redis默认推荐使用该配置。 appendfsync everysec #当redis突然运行崩溃时,会出现aof文件被截断的情况,Redis可以在发生这种情况时退出并加载错误,以下选项控制此行为。 #如果aof-load-truncated设置为yes,则加载截断的AOF文件,Redis服务器启动发出日志以通知用户该事件。 #如果该选项设置为no,则服务将中止并显示错误并停止启动。当该选项设置为no时,用户需要在重启之前使用“redis-check-aof”实用程序修复AOF文件在进行启动。 aof-load-truncated yes #yes开启AOF,no关闭AOF appendonly no #指定AOF文件名,4.0无法通过config set 设置,只能通过修改配置文件设置。 appendfilename appendonly.aof #RDB文件和AOF文件存放目录 dir /etc/redis ~~~ >[info]如果AOF和RDB都开启了,redis重启的时候,优先通过AOF进行数据恢复的,因为aof数据比较完整。 RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。 ## 三、RDB-AOF混合持久化 redis4.0相对与3.X版本其中一个比较大的变化是4.0添加了新的混合持久化方式。前面已经详细介绍了AOF持久化以及RDB持久化,这里介绍的混合持久化就是同时结合RDB持久化以及AOF持久化混合写入AOF文件。这样做的好处是可以结合 rdb 和 aof 的优点, 快速加载同时避免丢失过多的数据,缺点是 aof 里面的 rdb 部分就是压缩格式不再是 aof 格式,可读性差。 优点: * 混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。 缺点: * 兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,阅读性较差。 ### 1\. 混合持久化 4.0版本的混合持久化默认关闭的,通过aof-use-rdb-preamble配置参数控制,yes则表示开启,no表示禁用,默认是禁用的 ### 2\. 混合持久化过程 混合持久化同样也是通过bgrewriteaof完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据,如下图: ![](https://box.kancloud.cn/2ab86ad162aa4f0bb25db45725d0d3ed_487x609.png) ### 3\. 数据恢复 当我们开启了混合持久化时,启动redis依然优先加载aof文件,aof文件加载可能有两种情况如下: * aof文件开头是rdb的格式, 先加载 rdb内容再加载剩余的 aof。 * aof文件开头不是rdb的格式,直接以aof格式加载整个文件。