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

2024-03-26 05:36

本文主要是介绍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文件后台重写操作

这篇关于Redis中RDB的dirty机制和AOF中的后台重写机制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/847428

相关文章

redis群集简单部署过程

《redis群集简单部署过程》文章介绍了Redis,一个高性能的键值存储系统,其支持多种数据结构和命令,它还讨论了Redis的服务器端架构、数据存储和获取、协议和命令、高可用性方案、缓存机制以及监控和... 目录Redis介绍1. 基本概念2. 服务器端3. 存储和获取数据4. 协议和命令5. 高可用性6.

Spring排序机制之接口与注解的使用方法

《Spring排序机制之接口与注解的使用方法》本文介绍了Spring中多种排序机制,包括Ordered接口、PriorityOrdered接口、@Order注解和@Priority注解,提供了详细示例... 目录一、Spring 排序的需求场景二、Spring 中的排序机制1、Ordered 接口2、Pri

Redis的数据过期策略和数据淘汰策略

《Redis的数据过期策略和数据淘汰策略》本文主要介绍了Redis的数据过期策略和数据淘汰策略,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录一、数据过期策略1、惰性删除2、定期删除二、数据淘汰策略1、数据淘汰策略概念2、8种数据淘汰策略

Redis存储的列表分页和检索的实现方法

《Redis存储的列表分页和检索的实现方法》在Redis中,列表(List)是一种有序的数据结构,通常用于存储一系列元素,由于列表是有序的,可以通过索引来访问元素,因此可以很方便地实现分页和检索功能,... 目录一、Redis 列表的基本操作二、分页实现三、检索实现3.1 方法 1:客户端过滤3.2 方法

MySQL 缓存机制与架构解析(最新推荐)

《MySQL缓存机制与架构解析(最新推荐)》本文详细介绍了MySQL的缓存机制和整体架构,包括一级缓存(InnoDBBufferPool)和二级缓存(QueryCache),文章还探讨了SQL... 目录一、mysql缓存机制概述二、MySQL整体架构三、SQL查询执行全流程四、MySQL 8.0为何移除查

Python中操作Redis的常用方法小结

《Python中操作Redis的常用方法小结》这篇文章主要为大家详细介绍了Python中操作Redis的常用方法,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解一下... 目录安装Redis开启、关闭Redisredis数据结构redis-cli操作安装redis-py数据库连接和释放增

redis防止短信恶意调用的实现

《redis防止短信恶意调用的实现》本文主要介绍了在场景登录或注册接口中使用短信验证码时遇到的恶意调用问题,并通过使用Redis分布式锁来解决,具有一定的参考价值,感兴趣的可以了解一下... 目录1.场景2.排查3.解决方案3.1 Redis锁实现3.2 方法调用1.场景登录或注册接口中,使用短信验证码场

Redis 多规则限流和防重复提交方案实现小结

《Redis多规则限流和防重复提交方案实现小结》本文主要介绍了Redis多规则限流和防重复提交方案实现小结,包括使用String结构和Zset结构来记录用户IP的访问次数,具有一定的参考价值,感兴趣... 目录一:使用 String 结构记录固定时间段内某用户 IP 访问某接口的次数二:使用 Zset 进行

一文详解Java Condition的await和signal等待通知机制

《一文详解JavaCondition的await和signal等待通知机制》这篇文章主要为大家详细介绍了JavaCondition的await和signal等待通知机制的相关知识,文中的示例代码讲... 目录1. Condition的核心方法2. 使用场景与优势3. 使用流程与规范基本模板生产者-消费者示例

解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)

《解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)》该文章介绍了使用Redis的阻塞队列和Stream流的消息队列来优化秒杀系统的方案,通过将秒杀流程拆分为两条流水线,使用Redi... 目录Redis秒杀优化方案(阻塞队列+Stream流的消息队列)什么是消息队列?消费者组的工作方式每