设为首页 加入收藏

TOP

Redis源码剖析之持久化(一)
2018-11-20 22:09:14 】 浏览:319
Tags:Redis 源码 剖析 持久化

  Redis提供了两种持久化方式:RDB和AOF,下面,我们来看看上述两者的底层实现原理。


  在Redis中,有两种方式可以生成RDB文件,一个是SAVE,另一个是BGSAVE


  两者的主要区别是:SAVE命令在进行持久化操作的过程中,会阻塞Redis服务进行,也就是说,在以SAVE方式进行持久化操作的过程中,服务器不能再处理其他的命令请求,这个请求过程必须等到持久化操作结束;BGSAVE命令则是单独开启一个子进程来处理持久化操作


  上述过程用伪代码表现形式如下:


def save():
    rdbSave() # 将数据写入文件操作



def bgsave():
    # 创建子进程    pid = fork()


    if pid == 0:
        # 子进程负责创建RDB文件        rdbSave()
        # 完成之后向父进程发送信号        signal_parent()


    elif pid > 0:
        # 父进程继续处理命令请求,并通过轮询等待子进程信号        handle_request_and_wait_signal()
    else:
        # 处理出错情况        handle_fork_error()


  RDB文件的载入是在Redis服务器启动时,自动载入的,所以Redis并没有专门用于载入RDB文件的命令。只要服务器检测到有RDB文件的存在,它就会自动进行载入 操作。


  关于RDB文件载入过程,值得提一下就是,如果服务器开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据库。


  只有在AOF持久化功能处于关闭状态,Redis服务器才会使用RDBRDB文件来还原数据库状态。


  在执行save命令时,redis服务器会被阻塞,所以当save命令正在被执行时,客户端发送的所有命令请求都会被拒绝。


  在执行bgsave命令时,由于是子进程在处理持久化操作,所以Redis服务器可以继续处理客户的命令请求。但是,在执行bgsave命令期间,如果客户端又发送来了save,bgsave,bgrewrteaof三个命令其中一个,那么服务器的处理方式会有所不同。


  首先,bgsave 命令正在被子进程执行,那么客户端发来的save命令会直接被服务器拒绝,这是为了避免父进程与子进程同时执行两个rdbSave()调用,防止产生竞争条件。


  其次,bgsave 命令正在被子进程执行,那么客户端发来的bgsave命令也会直接被服务器拒绝,同样也是为了防止产生竞争条件。


  最后,bgsave命令和bgrewrteaof命令不能同时进行,如果bgsave命令正在执行,客户端的bgrewrteaof命令会延迟到bgsave命令执行完毕以后才会执行;如果bgrewrteaof命令正在被执行,那么客户端的bgsave命令会直接被服务器拒绝。这是因为,这两个命令都是由子进程来执行的,不能同时执行主要考虑到性能问题,试想两个并发执行的命令,同时进行大量的读写磁盘操作,这会大大降低服务器性能。


  上述我们讲到,save命令会阻塞服务器进程,而bgsave命令则会另启一个进程来执行持久化操作。


  因为bgsave命令可以在不阻塞服务器进程来进行持久化,所以redis允许用户通过设置服务器配置的save选项,来让redis间接性的自动执行bgsave命令。


  用户可以在redis.conf文件配置save保存规则,只要其中一个条件满足,服务器就会自动执行bgsave命令。


save 900 1 # 900秒之内,对数据库进行了一次修改就执行bgsave命令
save 300 10 # 300秒之内,对数据库进行了十次修改就执行bgsave命令
save 60 10000 # 60秒之内,对数据库进行了一万次修改就执行bgsave命令  接下来,我们来看看服务器是如何根据上述配置的规则,自动执行bgsave命令。


  我们来看看源码redis.h/redisServer,在这个大的结构体中存在如下一个字段:


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


  服务器会根据save选项所设置的保存条件,将该值设置到服务器redisServer结构的saveparams属性:


  saveparams属性是一个数组,数组每一个元素都是一个saveparam结构,每个结构都保存了一个save选择设置的保存条件:


struct saveparam{
    //秒数
    time_t seconds;
    //修改数
    int changes;
};


  上述结构体中的两个参数就是我们设置的,如:save 600 1; 那么seconds=600,changes=1。是不是很神奇!!


  如果有多个条件同时存在的话,那么它的结构如下:


  


  除了saveparms数组之外,服务器还维持着两个参数:dirty和lastsave.


  其中,dirty记录上一次执行save或者bgsave命令,服务器对数据库状态进行了多少次修改。lastsave则记录上一次执行save或者bgsave命令的时间。


struct redisServer{
    // 修改计数器    long long dirty;


    // 上一次执行保存的时间    time_t lastsave;
   
    struct saveparam *saveparams; //记录了保存条件的数组};


  说完了上述,接下来就来说说,redis服务器是如何发现该执行保存操作呢?


  在redis服务器启动之后,内部定期执行执行一个时间事件函数serverCron,这个函数默认每隔100毫秒就会执行一次,该函数用于对正在运行的服务器进行维护,其中一项工作就是检查save选项设置的保存条件是否满足,如果满足,就执行bgsave命令。


  伪代码如下:


def serverCron():
    # ...    # 遍历所有保存条件    for saveparam in server.saveparams:
        #计算具体上次执行保存操作有多少秒        save_interval = unixtime_now() - server.lastsave
        # 如果数据库状态的修改次数超过条件所设置的次数        # 并且距离上次保存的

首页 上一页 1 2 3 4 下一页 尾页 1/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Redis源码剖析之主从复制 下一篇关于MySQL的锁机制详解

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目