Redis之RDB和AOF持久化介绍

什么是数据库状态

  redis是一个键值对的数据库服务器,服务器中通常包含中任意个非空的数据库,而每个数据库又可以包含任意个键值对,为了方便起见,我们将服务器中的非空数据库以及他们的键值对统称为数据库状态。

RDB持久化的逻辑

  RDB持久化就可以手动执行也可以根据服务器配置选项定期的执行,该功能可以将某个时间点上的数据库状态保存到一个RDB文件中,这个文件其实是一个二进制的文件,通过这个文件可以还原生成RDB文件时的数据库状态。

RDB文件的创建

  有两个生成redisRDB文件的命令,一个是SAVE,一个是BGSAVE。SAVE命令是使用服务器进程来生成RDB文件,在生成RDB文件的时候,会阻塞所有的读写操作,服务器不能处理任何命令请求。BGSAVE命令是同过服务器进程派生出来一个子进程,然后有子进程负责创建RBD文件,服务器进程可以继续处理命令请求。

  其实创建RDB文件的工作都是通过rdbsave函数实现的是不过两种命令以不同的方式调用这个函数。

SAVE命令执行时的服务器状态

  其实这个很简单,我们上面已经提到过SAVE命令或阻塞一切请求,当然拥抱口BGSAVE请求了,所以在执行SAVE期间,再次执行BGSAVE命令会被直接拒绝

BGSAVE命令执行时的服务器状态

  我们知道BGSAVE命令是服务器的子进程来完成的,服务器进程可以继续接收和执行命令,但是再BGSAVE命令执行期间服务器处理SAVE、BGSAVE、BGREWRITEAOF三个命令的方式和平时有所不同,主要有一下几种情形:

  1.在BGSAVE命令执行期间,客户端发起的SAVE命令会被服务器拒绝。服务器禁止SAVE命令和BGDAVE命令同时执行是为了避免父进程和子进程同时执行两个rdbsave调用,防止产生竞争条件。

  2.在BGSAVE命令执行期间客户端发起BGSAVE命令会被服务器拒绝,因为同时执行两个BGSAVE命令也会产生竞争条件

  3.在BGSAVE命令执行期间,客户端发起BGREWRITEAOF命令会被延迟到BGSAVE命令执行完毕之后执行

  4.在BGREWRITEAOF命令执行期间,客户端发起BGSAVE命令会被服务器直接拒绝,主要是因为BGREWRITEAOF和BGSAVE命令都是由子进程来完成的,禁止他们同时执行知识一个性能方面的考虑,因为并发出两个子进程,并且这两个子进程同时进行大量的磁盘写入操作,并不是什么好事情。

RDB文件的载入

  对于RDB文件的载入就相对简单了,RDB文件的载入工作是在服务器启动的时候自动执行的,并且在服务器载入RDB文件期间,会一直处于阻塞状态,直到载入工作完成为止。

RDB自动间歇性保存

  上面我们讲了如何创建RDB文件,但是什么时候来创建RBD文件呢?除了手动的执行命令之外,我们还可以通过配置来让服务器自动来创建RDB文件,我们可以在redis的配置文件中通过以下配置来实现:

save    900    1
save    300    10
save     60     10000

  用户可以通过save选项设置多个保存条件(当然可以超过三个了,十个八个都可以,因为这些保存条件其实是保存在一个数组中的),但是只要其实任意一个条件满足,服务器就会执行BGSAVE命令。而上面的三条保存配置其实也是在我们开启了RDB持久化但是没有配置相关保存条件下时服务器给的默认的配置。

RDB自动保存的触发原理

  我们知道了如何配置保存条件的,但是这个保存条件是怎么触发的呢?

  其实服务器除了通过一个数据存储我们的保存条件外,还会维持一个dirty的计数器,以及一个lastsave属性,其中dirty计数器用来统计距离上一次成功执行SAVE命令或者BGSAVE命令后服务器对数据库状态进行了多少次修改(包括写入,删除,更新等操作),而lastsave属性是一个UNIX的时间戳,记录了服务器上一次成功执行SAVE命令或者BGSAVE命令的时间。这两个值都会在成功执行完BGSAVE命令后重置:dirty重置成0,lastsave更新为当前时间。

  而对于条件的判断则是Redis服务器会周期性的操作函数serverCron默认每隔100毫秒执行一次,该函数用于对正在运行的服务器进行维护,它的其中一项工作就是检查save选项所设置的保存条件是否满足,如果满足则执行BGSAVe命令。必须同时满足的条件是:1.距离上一次成功执行保存的时间超过设置的时间,2.数据库状态的修改次数超过设置的修改次数

RDB的文件结构

暂略

 AOF持久化的逻辑

  与RBD持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存redis服务器所执行的写命令来记录数据库状态的。

  被写入到AOF文件的命令都是以Redis的命令请求协议格式保存的,因为Redis的命令请求协议是纯文本的,所以我们可以直接打开一个AOF文件,里面保存的基本都是我们执行的命令,但是会有一些SELECT命令,SELECT命令是用于指定数据库的,此命令是服务器自动添加的。

AOF持久化的过程

  AOF持久化过程的实现可以分为命令追加、文件写入、文件同步三个步骤。

  命令追加:当AOF功能打开的时候,服务器执行一条命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的额末尾,但是aof_buf缓冲区的命令什么时候写入aof文件这个要根据额我们的appendfsync的相关配置来决定

  文件写入和同步:为了提高文件的写入效率,在现代的操作系统中,当用户调用write函数,将一些数据写入到文件的时候,操作系统通常会将写入数据暂时存放到一个内存缓冲区里面,等到缓冲区满了或者超过了执行的时限之后,才真正的将缓冲区中的数据写入到磁盘里面

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

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

  flushAppendOnlyFile函数的行为是有服务器配置文件的appendfsync选项的值来决定的,appendfsync的值有三种:

  1.always:将aof_buf缓冲区中的数据写入并同步到AOF文件。这种设置时最安全的,就变机器出现故障,也只是会丢失一个事件循环中所产生的命令数据

  2.everysec:将aof_buf缓冲区的所有内容写入到AOF文件,如果上次同步AOF文件的时间距离现在超过1s,那么再次对AOF文件进行同步,并且这个同步操作有一个线程专门完成。这种设置,服务器在每个事件循环中都要将aof_buf缓冲区中的所有内容写入到Aof文件,并且每隔1s就要在子线程中对AOF文件进行一次同步。从效率上来讲everysec模式足够快,并且就算出现故障停机,数据库也只丢失一秒钟的命令数据。

  3.no:在服务器的每个事件循环钟都要将aof缓冲区中的所有内容写入到AOF文件,但不对AOF文件进行同步,何时同步有操作系统来同步,所以这种设置是如果服务器停机,将会丢掉上次AOF文件同步后的所有写命令数据。

AOF文件的载入和数据还原

  因为AOF文件中包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一边AOF文件里面保存的写命令,就可以还原服务器关闭之前的数据状态。
AOF重写
   因为Aof持久化是通过保存被执行的写命令来记录数据库状态的,所以随着服务器事件的流逝,AOF文件内容会越来越大,文件体积也会也来越大,如果不加控制体积过大的AOF文件很可能对Redis服务器甚至宿主机造成影响。对于数据恢复来说,AOF文件中会有很多冗余的命令,比如对某个key操作了八次,AOF文件中会记录八条写命令,但是我们关心的只是最后一次命令执行后的数据状态,为了解决AOF文件体积膨胀问题,Redis提供了AOF重写功能。

  AOF重写没有对进行AOF任何操作,只是对AOF重写时候的数据状态转成写命令保存起来,也就是说重写后的AOF文件中保存着能够换肤重写时数据库状态的最小的写命令数,这样就可以大大的减少文件的体积了。

  需要注意的是在重写程序中在处理列表、哈希表、集合、有序集合这四种可能会带来多个元素的键时,会先检查包含元素的数量,如果数量超过了设置的常量值(这个值不通版本是不一样的),就会用多条命令来记录这个键而不单单时一条命令。

  因为Reids是单线程的,为了不是AOF重写时阻塞服务器的正常运行,redis决定将AOF重写放到一个子进程中进行,这样做有两个好处:

    1.子进程进行AOF重写期间,服务器进程(父进程)可以继续处理命令请求

    2.子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用所得情况下,保证数据得安全性

  有个问题我们要注意,在子进程AOF重写期间,服务器进程还需要继续处理命令请求,这个时候redis用来一个AOF重写缓冲区来解决这个问题,也就是说,在子进程AOF重写期间,服务器进程会进行三个工作:

    1.执行客户端发来得命令

    2.将执行后得写命令追加得AOF缓冲区

    3.将执行后得写命令追加得AOF重写缓冲区

  这样一来可以保证:

    1.AOF缓冲区得内容会定期被写入和同步到AOF文件,对现在AOF文件得处理工作会如常进行

    2.从创建子进程开始,服务器得所有写命令都会被记录到AOF重写缓冲区里面

  当子进程完成AOF重写后,会向服务器进程发一个信号,并调用一个信号处理函数执行一下工作:

    1.将Aof重写缓冲区中得所有内容写入到新得Aof文件中,这是新AOF文件所保存得数据库状态将和服务器当前得数据库状态保持一致

    2.对新的AOF文件进行改名,原子的覆盖现在的AOF文件,完成新旧文件的交替

  在整个AOF后台重写过程中,只有信号处理的时候会对服务器进程(父进程)造成阻塞,其他时候AOF后台重写都不会阻塞父进程,这将AOF重写对服务器造成的影响讲到了最低。

RDB持久化和AOF持久化的对比

RDB优劣势
优势:

  • RDB只代表某个时间点上的数据快照,所以适用于备份与全量复制,如一天进行备份一次。
  • Redis再加载RDB文件恢复数据远快于AOF文件
  • 性能上考虑RDB优于AOF,因为我们保存RDB文件只需fork一次子进程进行保存操作,父进程没有对磁盘I/O

劣势:

  • RDB没办法做到实时的持久化数据,因为fork是重量级别的操作,频繁执行成本过高
  • RDB需要经常fork子进程来保存数据集到磁盘,当数据集比较大额时候,fork的过程是比较耗时的,可能会导致redis在一些毫秒级不能响应客服端请求
  • 老版本的Redis无法兼容新版本的RDB文件

AOF优劣势
优势:

  • 通过配置同步策略基本能够达到实时持久化数据,如配置为everysec,则每秒同步一次AOF文件,也就是说最多丢失一秒钟的数据,兼顾了性能与数据的安全性
  • AOF 文件是一个只进行追加操作的日志文件(append only log), 因此对 AOF 文件的写入不需要进行 seek , 即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也可以轻易地修复这种问题。

劣势:

    • 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
    • 与AOF相比,在恢复大的数据时候,RDB方式更快一些

 

posted @ 2019-10-24 12:01  海棠--依旧  Views(395)  Comments(0Edit  收藏  举报