Redis中RDB的dirty机制和AOF中的后台重写机制

RDB的dirty计数器和lastsave属性

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

  • 1.dirty计数器记录距离上一次成功执行SAVE命令或者BGSAVE命令之后,服务器对数据库状态(服务器中的所有数据库)进行了多少次修改(包括写入、删除、更新等操作)
  • 2.lastsave属性是一个UNIX时间戳,记录了服务器上一次成功执行SAVE命令或者BGSAVE命令的时间
struct redisServer {
 // ...
 
 // 修改计数器
 long long dirty;
 // 上一次执行保存的时间
 time_t lastsave;
 // ...
}

当服务器成功执行一个数据库修改命令之后,程序就会对dirty计数器进行更新:命令修改了多少次数据库,dirty计数器的值就增加多少。

例子

  • 例如。如果我们为一个字符串键设置值:
127.0.0.1:6379> SET message "hello"
OK

那么程序会将dirty计数器的值增加1。

  • 又例如,如果像一个集合键增加三个新元素:
127.0.0.1:6379> SADD database Redis MongoDB MariaDB
(integer) 3

那么程序会将dirty计数器的值增加3。

  • 如图所示,该图展示了服务器状态中包含的dirty计数器和lastsave属性,
    说明如下:
    1.dirty计数器的值为123,表示服务器在上次保存之后对数据库状态共进行了123次修改
    2.lastsave属性则记录了服务器上次执行保存的时间1378270800
    在这里插入图片描述

检查保存条件是否满足

以下伪代码z展示了serverCron函数检查保存条件的过程:

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

程序会遍历并检查saveparams数组中的所有保存条件,只要有任意一个条件被满足,那么服务器就会执行BGSAVE命令。

例子

  • 举个例子,如果Redis服务器的当前状态如图所示.
    那么当时间来到1378271101,也即是1378270800的301秒之后,
    服务器将自动执行一次BGSAVE命令,因为saveparams数组的第二个保存条件——300秒之内有至少10次修改——已经被满足。
    在这里插入图片描述
    假设BGSAVE在执行5秒之后完成,那么如图所示的服务器状态将更新为如图所示。其中dirty计数器已经被重置为0,而lastsave属性也被更新为1378271106
    在这里插入图片描述

AOF文件重写的实现

虽然Redis将生成新AOF文件替换旧AOF文件的功能命名为"AOF文件重写",但实际上,AOF文件重写并不需要对现有的AOF文件进行任何读取、分析或者写入操作,这个功能是通过读取服务器当前的数据库状态来实现的。

例子

  • 举个例子,如果对服务器对list键执行了以下命令:
127.0.0.1:6379> RPUSH list "A" "B" // ["A", "B"]
(integer) 2
127.0.0.1:6379> RPUSH list "C" // ["A", "B", "C"]
(integer) 3
127.0.0.1:6379> RPUSH list "D" "E" // ["A", "B", "C", "D", "E"]
(integer) 5
127.0.0.1:6379> LPOP list // ["B", "C", "D", "E"]
"A"
127.0.0.1:6379> LPOP list // ["C", "D", "E"]
"B"
127.0.0.1:6379> RPUSH list "F" "G" // ["C", "D", "E", "F", "G"]
(integer) 5

那么服务器为了保存当前list键的状态,必须在AOF文件中写入六条命令。
如果服务器想要用尽量少的命令来记录list键的状态,那么最简单高效的办法不是去读取和分析现有的AOF文件的内容,而是直接从数据库中读取键list的值,然后用一条RPUSH list “C” “D” “E” “F” "G"命令来代替保存在AOF文件中的六条命令这样旧可以将保存list键所需的命令从六条减少为一条了

注意

在实际中,为了避免在执行命令时造成客户端输入缓冲区溢出,重写程序在处理列表、哈希表、集合、有序集合这四种可能会带有多个元素的键时,会先检查键所包含的元素数量,如果元素的数量超过了redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的
值,那么重写程序将使用多条命令来记录键的值,而不单单使用一条命令。REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值为64,这也就是说,如果一个集合键包含了超过64个元素,那么重写程序会用多条SADD命令来记录这个集合并且每条命令设置的元素数量也为64个:

SADD <set-key> <elem1><elem2>....<elem64>
SADD <set-key> <elem65><elem66>...<elem128>
SADD <set-key> <elem129><elem130>...<elem192>

另一方面如果一个列表键包含了超过64个项,那么重写程序会用多条RPUSH命令来保存这个集合,
并且每条命令设置的项数量也为64个

RPUSH <list-key> <item1><item2>...<item64>
RPUSH <list-key> <item65><item66>...<item128>
RPUSH <list-key> <item129><item130>...<item192>

重写程序使用类似的方法处理包含多个元素的有序集合键,以及包含多个键值对的哈希表键

AOF后台重写

虽然AOF重写程序aof_rewrite函数可以很好地完成创建一个新AOF文件的任务,但是,因为这个函数会进行大量的写入操作,所以调用这个函数的线程将被长时间阻塞,因为Redis服务器使用单个线程来处理命令请求,所以如果由服务器直接调用aof_rewrite函数的话,那么在重写AOF文件期间,服务器将无法处理客户端发来的命令请求。
很明显,作为一种辅佐性的维护手段,Redis不希望AOF重写造成服务器无法处理请求,所以Redis决定将AOF重写程序放到子进程里执行,这样做可以同时达到两个目的:

  • 1.子进程进行AOF重写期间,服务器进程(父进程)可以继续处理命令请求
  • 2.子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性。
    不过,使用子进程也有一个问题需要解决,因为子进程在进行AOF重写期间,服务器进程还需要继续处理命令请求,而新的命令可能会对现有的数据库状态进行修改,从而使得服务器当前的数据库状态和重写后的AOF文件所保存的数据库状态不一致。
    在这里插入图片描述

数据不一致问题

在这里插入图片描述

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

  • 1.执行客户端发来的命令
  • 2.将执行后的写命令追加到AOF缓冲区
  • 3.将执行后的写命令追加到AOF重写缓冲区
    这样一来可以保证:
  • 1.AOF缓冲区的内容会定期被写入和同步到AOF文件,对现有AOF文件的处理工作会如常进行
  • 2.从创建子进程开始,服务器执行的所有写命令都会被记录到AOF重写缓冲区里面当子进程完成AOF重写工作之后,它会向父进程发送一个信号,父进程在接到该信号之后,会调用一个信号处理函数,并执行以下共做:
  • 1.将AOF重写缓冲区中的所有内容写入到新的AOF文件中,这时新AOF文件所保存的数据库状态将和服务器当前的数据库状态一致
  • 2.对新的AOF文件进行改名,原子地(atomic)覆盖现有的AOF文件,完成新旧数据两个AOF文件的替换这个信号处理函数执行完毕之后,父进程就可以继续像往常一样接受命令请求了。

阻塞问题

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

例子

在这里插入图片描述

举个例子,图中展示i了一个AOF文件后台重写的执行过程:

  • 1.当子进程开始重写时,服务器进程(父进程)的数据库中只有一个k1的键,当子进程完成AOF文件重写之后,服务器进程的数据库中已经多处了k2、k3、k4三个新键
  • 2.在子进程向服务器发送信号之后,服务器进程会将保存在AOF重写缓冲区里面记录的k2/k3/k4三个键的命令追加到新AOF文件的末尾,然后用新的AOF文件替换旧文件完成AOF文件后台重写操作

相关推荐

  1. 【学习笔记】Redis-AOF日志机制

    2024-03-26 21:18:03       31 阅读
  2. Redis RDB AOF

    2024-03-26 21:18:03       39 阅读
  3. Redis持久化机制RDBAOF原理最佳实践

    2024-03-26 21:18:03       24 阅读
  4. redisAOF机制

    2024-03-26 21:18:03       48 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-26 21:18:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-26 21:18:03       100 阅读
  3. 在Django里面运行非项目文件

    2024-03-26 21:18:03       82 阅读
  4. Python语言-面向对象

    2024-03-26 21:18:03       91 阅读

热门阅读

  1. 本地项目内网穿透可公网访问包(备用)

    2024-03-26 21:18:03       38 阅读
  2. 【晴问算法】入门篇—字符串处理—单词倒序II

    2024-03-26 21:18:03       43 阅读
  3. num_set.insert(num) 和 num_set.count(num)

    2024-03-26 21:18:03       36 阅读
  4. 数据库的子查询

    2024-03-26 21:18:03       39 阅读
  5. Linux:ssh多台设备快速访问

    2024-03-26 21:18:03       36 阅读
  6. 版本转产关注点

    2024-03-26 21:18:03       31 阅读
  7. Oracle的Merge和Using

    2024-03-26 21:18:03       39 阅读
  8. sharding-mybatis分库分表

    2024-03-26 21:18:03       41 阅读
  9. 嵌入式学习day39 数据结构

    2024-03-26 21:18:03       32 阅读
  10. 面试 JVM 八股文十问十答第三期

    2024-03-26 21:18:03       42 阅读