Redis持久化原理随想

Redis持久化

   众所周知,Redis是内存数据库,且使用单个线程来处理命令请求。它将本身的数据库状态(非空数据库以及它们的键值对)存储在内存里面。因此若是没有持久化机制,不把数据保存到硬盘里面,那么一旦服务器进程退出,服务器中的数据库状态也会消失不见。

为了解决这个问题,redis 提供两种方法进行数据持久化,分别是RDB和AOF。RDB能够将Redis在内存中的数据库状态保存到磁盘里面以实现持久化,AOF经过记录写命令达到持久化效果。两种方法都有各自的优势,须要咱们在生产环境中依照实际的业务状况进行裁定。服务器按照下面的流程图选择持久化方式:

Redis  流程web

RDB 持久化

RDB持久化既能够手动执行,也能够依据配置文件选项按期执行。RDB持久化生成的RDB文件是一个二进制文件,经过此二进制文件可以还原数据库状态。
如图:

Redis RDBRDB 数据还原

RDB提供两种方式生成RDB文件,分别经过执行SAVE和BGSAVE命令生产RDB文件。SAVE由服务器进程直接执行保存,它会阻塞服务器的进程。BGSAVE是服务器子进程执行保存操做,它的运行不会阻塞服务器进程的运行。redis

SVAE 命令

上面已经说过,SAVE命令执行时会阻塞服务器的运行。所以,在服务器执行SAVE命令时,客户端发送的全部命令请求都会被拒绝。只有服务器执行完SAVE命令,服务器才会从新接收客户端发送的命令请求。数据库

BGSAVE 命令

与SAVE相反,BGSAVE由服务器子进程执行,在执行时依然能够接收客户端发送命令并处理。可是,服务器处理SAVE、BGSAVE、BGREWRITEAOF三种方式和平时有些不同。
数组

  1. 对于SAVE,在执行BGSAVE时,客户端发送的SAVE命令会被服务器拒绝。服务器禁止SAVE和BGSAVE命令同时执行是为了防止父进程(服务器进程)和子进行同时执行产生竞争条件
    安全

  2. 对于BGSAVE。在执行BGSAVE时,客户端发送的BGSAVE命令时一样也是被拒绝。缘由是为了防止两个BGSAVE产生竞争条件。
    服务器

  3. 对于BGREWRITEAOF。在执行BGSAVE时,也是被拒绝的。缘由是由于BGSAVE和BGREWRITEAOF不能同时执行。
    app

按期持久化原理

在陈述原理以前,先来看下几个与按期持久化密切相关的属性。
函数

  1. saveparams属性

    在服务启动后,服务器会读取save的值赋给saveparams。save 是redis.conf中的一个配置文件,它在服务器中的默认配置为:性能

save 900 1       //服务器在900秒以内,对数据库进行了至少1次修改。
save 300 10           //服务器在300秒以内,对数据库进行了至少10次修改。
save 60 10000         //服务器在60秒以内,对数据库进行了至少10000次修改。

服务器状态redisServer中saveparams结构以下:
spa

struct redisServer {    

           // ...    

           // 记录了保存条件的数组    
           struct saveparam *saveparams;   

           // ...
      };

     struct saveparam {

          // 秒数
          time_t seconds;    

          // 修改数
         int changes;
      };

若是服务器中SAVE属性为默认配置,那么服务器中的状态将会下面这样的。

RDB 状态

  1. dirty计数器和lastsave属性

    除了saveparams数组以外,服务器状态还维持着一个dirty计数器,以及一个lastsave属性:

  • dirty计数器记录距离上一次成功执行SAVE命令或者BGSAVE命令以后,服务器对数据库状态(服务器中的全部数据库)进行了多少次修改(包括写入、删除、更新等操做)。

  • lastsave属性是一个UNIX时间戳,记录了服务器上一次成功执行SAVE命令或者BGSAVE命令的时间。

    dirty和lastsave在服务器状态中的结构以下:

struct redisServer {    

           // ...    

           // 修改计数器  
            long long dirty;  

           // 上一次执行保存的时间
             time_t lastsave;  

           // ...
      };



上面已经介绍完几个重要的属性了,如今开始切入正题了。

若是未开启AOF功能,那么在Redis启动后,Redis的服务器周期性操做函数serverCron默认每隔100毫秒就会执行一次,它的其中一项工做就是检查save选项所设置的保存条件是否已经知足。若是知足的话,就执行BGSAVE命令。下面是ServerCron函数的逻辑代码

def serverCron():    
# …    
    # 遍历全部保存条件    
    for saveparam in server.saveparams:       
         # 计算距离上次执行保存操做有多少秒       
         save_interval = unixtime_now()-server.lastsave    
    
        # 若是数据库状态的修改次数超过条件所设置的次数       
        # 而且距离上次保存的时间超过条件所设置的时间        
        # 那么执行保存操做       
         if      server.dirty >= saveparam.changes and \
                   save_interval > saveparam.seconds:       
     
                BGSAVE(); 
     # ...

举个例子,若是Redis服务器的当前状态以下图所示


那么当时间来到1378271101,也便是1378270800的301秒以后,服务器将自动执行一次BGSAVE命令,由于saveparams数组的第二个保存条件——300秒以内有至少10次修改——已经被知足。

假设BGSAVE在执行5秒以后完成,那么上图所示的服务器状态将更新为下图所示的状态,其中dirty计数器已经被重置为0,而lastsave属性也被更新为1378271106。

FINISHED

以上就是Redis服务器根据save选项所设置的保存条件,自动执行BGSAVE命令,进行间隔性数据保存的实现原理。

AOF 持久化

AOF持久化是经过保存Redis服务器所执行的写命令来记录数据库状态的。AOF持久化命令能够分为命令追加(append)、文件写入、文件同步三个步骤。开启AOF持久化的配置
appendonly yes

命令追加

resist服务器中的结构是这样的

struct redisServer {
     // ...

     // AOF缓冲区
     sds aof_buf;

     // ...
}

服务器在执行一个写命令后,会将命令追加到 aof_buf 缓冲区的末尾。

命令的写入

将命令写入到aof文件中去

将aof文件中的命令同步到磁盘

appendfsync配置说明

  1. appendfsync no (同步操做交给数据库)
    当设置appendfsync为no的时候,Redis不会主动调用fsync去将AOF日志内容同步到磁盘,因此这一切就彻底依赖于操做系统的调试了。对大多数Linux操做系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。

  2. appendfsync everysec (每隔一秒执行一次同步操做)
    当设置appendfsync为everysec的时候,Redis会默认每隔一秒进行一次fsync调用,将缓冲区中的数据写到磁盘。可是当这一次的fsync调用时长超过1秒时。Redis会采起延迟fsync的策略,再等一秒钟。也就是在两秒后再进行fsync,这一次的fsync就无论会执行多 长时间都会进行。这时候因为在fsync时文件描述符会被阻塞,因此当前的写操做就会阻塞。

    结论就是,在绝大多数状况下,Redis会每隔一秒进行一 次fsync。在最坏的状况下,两秒钟会进行一次fsync操做。这一操做在大多数数据库系统中被称为group commit,就是组合屡次写操做的数据,一次性将日志写到磁盘。
  3. appendfsync always (每一次写操做都会执行同步操做) 置appendfsync为always时,每一次写操做都会调用一次fsync,这时数据是最安全的,固然,因为每次都会执行fsync,因此其性能也会受到响。

相关文章
相关标签/搜索