1、什么是Redis持久化?

持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。

2、为什么需要持久化?

Redis对数据的操作都是基于内存的,当遇到了进程退出、服务器宕机等意外情况,如果没有持久化机制,那么Redis中的数据将会丢失无法恢复。有了持久化机制,Redis在下次重启时可以利用之前持久化的文件进行数据恢复。

3、Redis 的持久化机制是什么?

redis提供了RDB(默认) 和 AOF两种持久化机制。

4、RDB持久化

RDB就是将数据库某一时刻的状态(将服务器中的非空数据库和它们的键值对统称为数据库状态)保存到一个rdb文件中,生成的rdb文件是一个经过压缩的二进制文件。RDB持久化既可以手动执行也可以根据服务器配置自动执行。

1、手动执行:运行save或者bgsave 命令、shutdown命令也触发save命令并在SAVE命令执行完毕之后关闭服务器、当一个Redis服务器连接另一个Redis服务器,并向对方发送SYNC命令来开始一次复制操作的时候会触发bgsave命令。

实际上创建rdb文件的工作是由rdb.c/rdbsave函数完成的;save和bgseve只是以不同的方式来调用这个函数;通过下面的伪代码来看一下;

save(){
     //创建rdb文件
     rdbSave();
 }bgSave(){
     //先创建子进程,如果在父进程中,fork返回新创建子进程的进程ID;
     //在子进程中,fork返回0;
     //如果出现错误,fork返回一个负值;
     pid = fork()  
     if(pid == 0){
         //由子进程创建rdb文件
         rdbSave();
         //完成后向父进程发送信号
         singnal_parent();
     }else if(pid > 0){
         //父进程继续处理命令请求,并通过轮询等待子进程的信号
         handle_request_and_wait_signal();
     }else{
         //处理出错情况
         handle_fork_error();
     }
 }

SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间,服务器不能处理任何命令请求;

和SAVE命令直接阻塞服务器进程的做法不同,BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求;过程如下:

为什么需要使用redis 发布订阅 redis为什么需要持久化_持久化

原理:redis 在持久化时会调用 glibc 的函数fork产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段。这是 Linux 操作系统的机制,为了节约内存资源。子进程做持久化,不会修改内存数据结构,只是对数据遍历读取、序列化、写磁盘;父进程接受客户端请求,对内存数据结构不断修改。这个时候就会使用操作系统的 COW (copy-on-write)机制来进行数据段页面的分离。数据段是由很多操作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据就凝固了,再也不会改变,这也是被称为快照的原因,接下来子线程可以遍历数据,序列化到磁盘。

在  SAVE 命令命令执行期间,客户端发送的所有命令请求都会被拒绝。

在 BGSAVE 命令执行期间,服务器处理SAVE、BGSAVE、BGREWRITEAOF三个命令的方式会和平时有所不同。

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

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

最后,BGREWRITEAOF 和 BGSAVE 两个命令不能同时执行。如果 BGSAVE 命令正在执行,那么客户端发送的 BGREWRITEAOF 命令会被延迟到 BGSAVE 命令执行完毕之后执行。

如果BGREWRITEAOF命令正在执行,那么客户端发送的BGSAVE命令会被服务器拒绝。因为BGREWRITEAOF和BGSAVE两个命令的实际工作都由子进程执行,所以这两个命令在操作方面并没有什么冲突的地方,不能同时执行它们只是一个性能方面的考虑——并发出两个子进程,并且这两个子进程都同时执行大量的磁盘写入操作,这怎么想都不会是一个好主意。

2、自动触发:如果用户在redis.conf中设置了save配置选项,比如save 60 10000,那么从Redis最近一次创建快照之后开始算起,当“60秒之内有10000次写入”这个条件被满足时,Redis就会自动触发BGSAVE命令。如果用户设置了多个 save配置选项,那么当任意一个save配置选项所设置的条件被满足时,Redis就会触发一次 BGSAVE命令,因为BGSAVE命令可以在不阻塞服务器进程的情况下执行,所以Redis 允许用户通过设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令。

如果我们在服务器(redis.conf)做以下配置:

save 900 1    #服务器在900秒之内对数据库进行了至少1次修改

save 300 10    #服务器在300秒之内,对数据库进行了至少10次修改。

save 60 10000  #服务器在60秒之内,对数据库进行了至少10000次修改。

那么只要满足以下三个条件中的任意一个,BGSAVE命令就会被执行。

5、AOF持久化

简单来说,AOF 持久化会将被执行的写命令写到AOF文件的末尾,以此来记录数据发生的变化。(命令请求会先保存到AOF缓冲区里面,之后再定期写入并同步到AOF文件)。Redis只要从头到尾重新执行一次AOF 文件包含的所有写命令,就可以恢复AOF文件所记录的数据集。如图所示:

为什么需要使用redis 发布订阅 redis为什么需要持久化_子进程_02

AOF持久化功能可以分为3个部分:命令追加、文件写入、文件同步。

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

2、文件写入:Redis 的服务器进程就是一个事件循环(loop ),这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复,而时间事件则负责执行像servercron函数这样需要定时运行的函数。因为服务器在处理文件事件时可能会执行写命令,使得一些内容被追加到aof缓冲区里面,所以在服务器每次结束一个事件循环之前,它都会调用 flushAppendonlyFile 函数,考虑是否需要将aof缓冲区中的内容写入和保存到AOF文件里面。flushAppendOnlyFile函数的行为由服务器配置的appendfsync选项的值来决;

appendfsync的选项:

always:服务器在每个事件循环(每个新命令执行)都要将aof缓冲区中的所有内容写入并同步到aof文件 :非常慢,但是最安全的。

everysec:服务器在每个事件循环都要将aof缓冲区中的所有内容写人到AOF文件,如果上次同步AOF文件的时间距离现在超过一秒钟,那么再次对AOF文件进行同步,并且这个同步操作是由一个子线程专门负责执行的;

no:服务器在每个事件循环都要将aof缓冲区中的所有内容写入到AOF 文件,但并不对AOF 文件进行同步,何时同步由操作系统来决定;

6、AOF重写

因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。

举个例子: 如果你对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录(entry)。然而在实际上, 只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。

为了解决AOF 文件体积不断增大的问题,用户通过移除AOF文件中的冗余命令来重写( rewrite )AOF 文件,使AOF文件的体积变得尽可能地小。

手动触发:直接调用bgrewriteaof命令。

自动触发:更具auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机。

面临的问题:

  • BGREWR工TEAOF的工作原理和 BGSAVE创建快照的工作原理非常相似:Redis 会创建一个子进程,然后由子进程负责对AOF 文件进行重写。因为AOF文件重写也需要用到子进程,所以快照持久化因为创建子进程而导致的性能问题和内存占用问题,在AOF 持久化中也同样存在。更糟糕的是,如果不加以控制的话,AOF 文件的体积可能会比快照文件的体积大好几倍,在进行AOF重写并删除旧AOF 文件的时候,删除一个体积达到数十GB 大的旧 AOF 文件可能会导致操作系统挂起(hang)数秒。
  • 子进程也有一个问题需要解决,因为子进程在进行AOF重写期间,服务器进程还需要继续处理命令请求,而新的命令可能会对现有的数据库状态进行修改,从而使得服务器当前的数据库状态和重写后的AOF文件所保存的数据库状态不一致。

为了解决这种数据不一致问题,Redis服务器设置了一个AOF重写缓冲区,这个缓冲区在服务器创建子进程之后开始使用,当Redis服务器执行完一个写命令之后,它会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区这,也就是说,在子进程执行AOF重写期间,服务器进程需要执行以下三个工作:

1、执行客户端发来的命令。

2、将执行后的写命令追加到AOF缓冲区。

3、将执行后的写命令追加到AOF重写缓冲区。

为什么需要使用redis 发布订阅 redis为什么需要持久化_为什么需要使用redis 发布订阅_03

AOF缓冲区的内容会定期被写入和同步到AOF文件,对现有AOF文件的处理工作会如常进行。从创建子进程开始,服务器执行的所有写命令都会被记录到AOF重写缓冲区里面。当子进程完成AOF重写工作之后,它会向父进程发送一个信号,父进程在接到该信号之后,会调用一个信号处理函数,并执行以下工作:

1、将AOF重写缓冲区中的所有内容写人到新AOF文件中,这时新AOF文件所保存的数据库状态将和服务器当前的数据库状态一致。

2、对新的AOF文件进行改名,原子地( atomic)覆盖现有的AOF文件,完成新旧两个AOF文件的替换。

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

7、 RDB与AOF 对比

1、同时启用时优先使用AOF,因为AOF可以带来更高的数据安全性。

2、与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些。在一般情况下, AOF 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。

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

二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。无论是使用AOF持久化还是快照持久化,将数据持久化到硬盘上都是非常有必要的,但除了进行持久化之外,用户还必须对持久化所得的文件进行备份(最好是备份到多个不同的地方),这样才能尽量避免数据丢失事故发生。如果条件允许的话,最好能将快照文件和最新重写的AOF文件备份到不同的服务器上面。
通过使用AOF持久化或者快照持久化,用户可以在系统重启或者崩溃的情况下仍然保留数据。随着负载量的上升,或者数据的完整性变得越来越重要时,用户可能需要使用复制特性。