Redis系列(三):Redis的持久化机制(RDB、AOF)

本篇博客是Redis系列的第3篇,主要讲解下Redis的2种持久化机制:RDB和AOF。html

本系列的前2篇能够点击如下连接查看:面试

Redis系列(一):Redis简介及环境安装数据库

Redis系列(二):Redis的5种数据结构及其经常使用命令安全

1. 为何须要持久化?

由于Redis是内存数据库,它将本身的数据存储在内存里面,一旦Redis服务器进程退出或者运行Redis服务器的计算机停机,Redis服务器中的数据就会丢失。服务器

为了不数据丢失,因此Redis提供了持久化机制,将存储在内存中的数据保存到磁盘中,用于在Redis服务器进程退出或者运行Redis服务器的计算机停机致使数据丢失时,快速的恢复以前Redis存储在内存中的数据。网络

Redis提供了2种持久化方式,分别为:数据结构

  1. RDB持久化
  2. AOF持久化

接下来,咱们一一详解。app

2. RDB持久化

RDB持久化是将某个时间点上Redis中的数据保存到一个RDB文件中,以下所示:操作系统

基于RDB持久化的上述性质,因此RDB持久化也叫作快照持久化。线程

该文件是一个通过压缩的二进制文件,经过该文件能够还原生成RDB文件时Redis中的数据,以下所示:

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文件会根据Redis配置文件中的名称和路径来保存,相关的2个配置以下所示:

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

2.2 载入RDB文件

首先,咱们要明确的是,载入RDB文件的目的是为了在Redis服务器进程从新启动以后还原以前存储在Redis中的数据。

而后,Redis载入RDB文件并无专门的命令,而是在Redis服务器启动时自动执行的。

并且,Redis服务器启动时是否会载入RDB文件还取决于服务器是否启用了AOF持久化功能,具体判断逻辑为:

  1. 只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据。
  2. 若是服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据。

以上判断逻辑以下图所示:

默认状况下,Redis服务器的AOF持久化功能是关闭的,因此Redis服务器在启动时会载入RDB文件,

启动日志以下所示:

2.3 服务器状态

建立和载入RDB文件,可能存在的服务器状态有如下3种:

  1. 当执行SAVE命令时,Redis服务器会被阻塞,此时客户端发送的全部命令请求都会被阻塞,只有在服务器执行完SAVE命令,从新开始接受命令请求以后,客户端发送的命令请求才会被处理。
  2. 当执行BGSAVE命令时,Redis服务器不会被阻塞,Redis服务器仍然能够继续处理客户端发送的命令请求。
  3. 服务器在载入RDB文件期间,会一直处于阻塞状态,直到RDB文件载入成功。

3. AOF持久化

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

默认状况下,AOF持久化功能是关闭的,若是想要打开,能够修改下图所示的配置:

举个例子,假设Redis中尚未存储任何数据,咱们执行了以下所示的命令:

而后咱们会发现Redis服务器生成了1个名为appendonly.aof的文件,打开该文件,咱们能够看到上面执行的3个写命令都存储在该文件中:

3.1 AOF持久化的实现

当AOF持久化功能处于打开状态时,Redis服务器在执行完一个写命令以后,会以协议格式(如上面截图中AOF文件里保存写命令的格式)将被执行的写命令追加到服务器状态的AOF缓冲区的末尾,而后Redis服务器会根据配置文件中appendfsync选项的值来决定什么时候将AOF缓冲区中的内容写入和同步到AOF文件里面。

appendfsync选项有如下3个值:

  1. always

    从安全性来讲,always是最安全的(丢失数据最少),由于即便出现故障停机,数据库也只会丢失一个事件循环中所产生的命令数据。

    从效率来讲,always的效率最慢,由于服务器在每一个事件循环都要将AOF缓冲区中的全部内容写入到AOF文件,而且同步AOF文件。

  2. everysec

    从安全性来讲,everysec模式下,即便出现故障停机,数据库只会丢失一秒钟的命令数据。

    从效率来讲,everysec模式足够快,由于服务器在每一个事件循环都要将AOF缓冲区中的全部内容写入到AOF文件,而且每隔一秒就要在子线程中对AOF文件进行同步。

  3. no

    从安全性来讲,no模式下,若是出现故障停机,数据库会丢失上次同步AOF文件以后的全部写命令数据,具备不肯定性,由于服务器在每一个事件循环都要将AOF缓冲区中的全部内容写入到AOF文件,至于什么时候对AOF文件进行同步,则由操做系统控制。

    从效率来讲,no模式和everysec模式的效率差很少。

appendfsync选项的默认值是everysec,也推荐使用这个值,由于既保证了效率又保证了安全性。

3.2 载入AOF文件

由于AOF文件包含了重建数据库所需的全部写命令,因此Redis服务器只要读入并从新执行一遍AOF文件里面保存的写命令,就能够还原Redis服务器关闭以前的数据。

Redis读取AOF文件并还原数据库的详细步骤以下:

  1. 建立一个不带网络链接的伪客户端

    由于Redis的命令只能在客户端上下文中执行,而载入AOF文件时所使用的命令直接来源于AOF文件而不是网络链接,因此服务器使用了一个没有网络链接的伪客户端来执行AOF文件保存的写命令。

    伪客户端执行命令的效果和带网络链接的客户端执行命令的效果彻底同样。

  2. 从AOF文件中分析并读取出一条写命令。

  3. 使用伪客户端执行被读取出的写命令。

  4. 一直执行步骤2和步骤3,直到AOF文件中的全部写命令都被执行完毕。

以上步骤以下图所示:

若是Redis服务器开启了AOF持久化功能,那么Redis服务器在启动时会载入AOF文件,

启动日志以下所示:

3.3 AOF重写

由于AOF持久化是经过保存被执行的写命令来记录数据库数据的,因此随着Redis服务器运行时间的增长,AOF文件中的内容会愈来愈多,文件的体积会愈来愈大,若是不作控制,会有如下2点坏处:

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

举个例子,在客户端执行以下命令:

为了记录这个list键的状态,AOF文件就须要保存上面执行的6条命令。

为了解决AO文件体积愈来愈大的问题,Redis提供了AOF文件重写功能,即Redis服务器会建立一个新的AOF文件来替代现有的AOF文件,新旧两个AOF文件所保存的数据库数据相同,但新AOF文件不会包含任何浪费空间的冗余命令,因此新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文件的体积。

综上所述,AOF文件重写功能的实现原理为:

首先从数据库中读取键如今的值,而后用一条命令去记录键值对,代替以前记录这个键值对的多条命令。

3.3.2 AOF后台重写

由于AOF文件重写会进行大量的文件写入操做,因此执行这个操做的线程将被长时间阻塞。

由于Redis服务器使用单个线程来处理命令请求,因此若是由服务器进程直接执行这个操做,那么在重写AOF文件期间,服务器将没法处理客户端发送过来的命令请求。

为了不上述问题,Redis将AOF文件重写功能放到子进程里执行,这样作有如下2个好处:

  1. 子进程进行AOF文件重写期间,服务器进程(父进程)能够继续处理命令请求。
  2. 子进程带有服务器进程的数据副本,使用子进程而不是线程,能够在避免使用锁的状况下,保证数据的安全性。

AOF后台重写的步骤以下所示:

  1. 服务器进程建立子进程,子进程开始AOF文件重写

  2. 从建立子进程开始,服务器进程执行的全部写命令不只要写入AOF缓冲区,还要写入AOF重写缓冲区

    写入AOF缓冲区的目的是为了同步到原有的AOF文件。

    写入AOF重写缓冲区的目的是由于子进程在进行AOF文件重写期间,服务器进程还在继续处理命令请求,

    而新的命令可能会对现有的数据库进行修改,从而使得服务器当前的数据库数据和重写后的AOF文件所

    保存的数据库数据不一致。

  3. 子进程完成AOF重写工做,向父进程发送一个信号,父进程在接收到该信号后,会执行如下操做:

    1.将AOF重写缓冲区中的全部内容写入到新AOF文件中,这样就保证了新AOF文件所保存的数据库数据和服务器当前的数据库数据是一致的。

    2.对新的AOF文件进行更名,原子地覆盖现有的AOF文件,完成新旧两个AOF文件的替换。

Redis提供了BGREWRITEAOF命令来执行以上步骤,以下图所示:

执行完成后,打开appendonly.aof文件,发现保存list键的命令从六条变为了一条:

除了手动执行BGREWRITEAOF命令外,Redis还提供了2个配置项用来自动执行BGREWRITEAOF命令:

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb

该配置表示,当AOF文件的体积大于64MB,而且AOF文件的体积比上一次重写以后的体积大了至少一倍(100%),Redis将自动执行BGREWRITEAOF命令。

4. RDB持久化、AOF持久化的区别

经过上面的讲解,咱们会发现Redis提供的2种持久化方法是有区别的,能够总结为如下4点:

  1. 实现方式
  2. 文件体积
  3. 安全性
  4. 优先级

接下来一一讲解。

4.1 实现方式

RDB持久化是经过将某个时间点Redis服务器存储的数据保存到RDB文件中来实现持久化的。

AOF持久化是经过将Redis服务器执行的全部写命令保存到AOF文件中来实现持久化的。

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设计与实现》

相关文章
相关标签/搜索