
1、为什么需要持久化?
RDB持久化
AOF持久化
RDB持久化是将某个时间点上Redis中的数据保存到一个RDB文件中,如下所示:

基于RDB持久化的上述性质,所以RDB持久化也叫做快照持久化。

2.1 创建RDB文件
Redis提供了2个命令来创建RDB文件,一个是SAVE,另一个是BGSAVE。
SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求,如下所示:

BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求,如下所示:

以上描述也是这2个命令的区别,这里是重点,面试经常会问到。
因为BGSAVE命令可以在不阻塞服务器进程的情况下执行,所以推荐使用BGSAVE命令。
我们可以手动执行该命令,如上面截图所示,但还是推荐设置下Redis服务器配置文件的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令。
我们可以通过save选项设置多个保存条件,只要其中任意一个条件被满足,服务器就会执行BGSAVE命令。
save选项设置的默认条件如下所示:
save 900 1
save 300 10
save 60 10000

默认的配置条件表示,只要满足以下3个条件中的任意1个,BGSAVE命令就会被执行:
服务器在900s(即15分钟)之内,对数据库进行了至少1次修改
服务器在300s(即5分钟)之内,对数据库进行了至少10次修改
服务器在60s(即1分钟)之内,对数据库进行了至少10000次修改
当满足条件执行BGSAVE命令时,输出日志如下图所示:


最终生成的RDB文件如下所示(截图为本机Windows环境,Linux环境下路径会稍有不同):

2.2 载入RDB文件
只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据。
如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据。
以上判断逻辑如下图所示:


2.3 服务器状态
创建和载入RDB文件,可能存在的服务器状态有以下3种:
当执行SAVE命令时,Redis服务器会被阻塞,此时客户端发送的所有命令请求都会被阻塞,只有在服务器执行完SAVE命令,重新开始接受命令请求之后,客户端发送的命令请求才会被处理。
当执行BGSAVE命令时,Redis服务器不会被阻塞,Redis服务器仍然可以继续处理客户端发送的命令请求。
服务器在载入RDB文件期间,会一直处于阻塞状态,直到RDB文件载入成功。


AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库数据的,如下图所示:

默认情况下,AOF持久化功能是关闭的,如果想要打开,可以修改下图所示的配置:

举个例子,假设Redis中还没有存储任何数据,我们执行了如下所示的命令:



3.1 AOF持久化的实现
1、always
从安全性来说,always是最安全的(丢失数据最少),因为即使出现故障停机,数据库也只会丢失一个事件循环中所产生的命令数据。
从效率来说,always的效率最慢,因为服务器在每个事件循环都要将AOF缓冲区中的所有内容写入到AOF文件,并且同步AOF文件。
2、everysec
从安全性来说,everysec模式下,即使出现故障停机,数据库只会丢失一秒钟的命令数据。
从效率来说,everysec模式足够快,因为服务器在每个事件循环都要将AOF缓冲区中的所有内容写入到AOF文件,并且每隔一秒就要在子线程中对AOF文件进行同步。
3、no
从安全性来说,no模式下,如果出现故障停机,数据库会丢失上次同步AOF文件之后的所有写命令数据,具有不确定性,因为服务器在每个事件循环都要将AOF缓冲区中的所有内容写入到AOF文件,至于何时对AOF文件进行同步,则由操作系统控制。
从效率来说,no模式和everysec模式的效率差不多。

3.2 载入AOF文件
Redis读取AOF文件并还原数据库的详细步骤如下:
创建一个不带网络连接的伪客户端
因为Redis的命令只能在客户端上下文中执行,而载入AOF文件时所使用的命令直接来源于AOF文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行AOF文件保存的写命令。
伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样。
从AOF文件中分析并读取出一条写命令。
使用伪客户端执行被读取出的写命令。
一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被执行完毕。
以上步骤如下图所示:

如果Redis服务器开启了AOF持久化功能,那么Redis服务器在启动时会载入AOF文件,
启动日志如下所示:

3.3 AOF重写
过多的占用服务器磁盘空间,可能会对Redis服务器甚至整个宿主计算机造成影响。 AOF文件的体积越大,使用AOF文件来进行数据库还原所需的时间就越多。

3.3.1 AOF重写的实现原理
AOF文件重写并不需要对现有的AOF文件进行任何读取、分析或者写入操作,而是通过读取服务器当前的数据库数据来实现的。
仍然以上面的list键为例,旧的AOF文件保存了6条命令来记录list键的状态,但list键的结果是“C” "D" "E" "F" "G"这样的数据,所以AOF文件重写时,可以用一条RPUSH list “C” "D" "E" "F" "G"
命令来代替之前的六条命令,这样就可以将保存list键所需的命令从六条减少为一条了。
按照上面的原理,如果Redis服务器存储的键值对足够多,AOF文件重写生成的新AOF文件就会减少很多很多的冗余命令,进而大大减小了AOF文件的体积。
3.3.2 AOF后台重写
因为AOF文件重写会进行大量的文件写入操作,所以执行这个操作的线程将被长时间阻塞。
因为Redis服务器使用单个线程来处理命令请求,所以如果由服务器进程直接执行这个操作,那么在重写AOF文件期间,服务器将无法处理客户端发送过来的命令请求。
为了避免上述问题,Redis将AOF文件重写功能放到子进程里执行,这样做有以下2个好处:
子进程进行AOF文件重写期间,服务器进程(父进程)可以继续处理命令请求。 子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性。
AOF后台重写的步骤如下所示:
服务器进程创建子进程,子进程开始AOF文件重写
从创建子进程开始,服务器进程执行的所有写命令不仅要写入AOF缓冲区,还要写入AOF重写缓冲区
写入AOF缓冲区的目的是为了同步到原有的AOF文件。
写入AOF重写缓冲区的目的是因为子进程在进行AOF文件重写期间,服务器进程还在继续处理命令请求,
而新的命令可能会对现有的数据库进行修改,从而使得服务器当前的数据库数据和重写后的AOF文件所
保存的数据库数据不一致。
子进程完成AOF重写工作,向父进程发送一个信号,父进程在接收到该信号后,会执行以下操作:
1.将AOF重写缓冲区中的所有内容写入到新AOF文件中,这样就保证了新AOF文件所保存的数据库数据和服务器当前的数据库数据是一致的。
2.对新的AOF文件进行改名,原子地覆盖现有的AOF文件,完成新旧两个AOF文件的替换。
Redis提供了BGREWRITEAOF
命令来执行以上步骤,如下图所示:


BGREWRITEAOF命令外,Redis还提供了2个配置项用来自动执行
BGREWRITEAOF命令:
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

该配置表示,当AOF文件的体积大于64MB,并且AOF文件的体积比上一次重写之后的体积大了至少一倍(100%),Redis将自动执行BGREWRITEAOF命令。
实现方式 文件体积 安全性 优先级
4.1 实现方式
4.2 文件体积
由上述实现方式可知,RDB持久化记录的是结果,AOF持久化记录的是过程,所以AOF持久化生成的AOF文件会有体积越来越大的问题,Redis提供了AOF重写功能来减小AOF文件体积。
4.3 安全性
AOF持久化的安全性要比RDB持久化的安全性高,即如果发生机器故障,AOF持久化要比RDB持久化丢失的数据要少。
因为RDB持久化会丢失上次RDB持久化后写入的数据,而AOF持久化最多丢失1s之内写入的数据(使用默认everysec配置的话)。
4.4 优先级
由于上述的安全性问题,如果Redis服务器开启了AOF持久化功能,Redis服务器在启动时会使用AOF文件来还原数据,如果Redis服务器没有开启AOF持久化功能,Redis服务器在启动时会使用RDB文件来还原数据,所以AOF文件的优先级比RDB文件的优先级高。
5、源码及参考
Josiah L. Carlson 《Reids实战》
黄健宏 《Redis设计与实现》
作者:申城异乡人
链接:https://juejin.im/post/5e426906f265da571c4c8a49



麻烦点一下在看再走呗





