深入了解Redis——持久化

一,Redis持久化

Redis持久化即将内存中的数据持久化到磁盘中,在下一次重启后还能进行使用,Redis持久化分为RDBAOF两种,我们接下来分别介绍RDB和AOF的内部原理和区别

RDB

Redis运行时会将当前的内存快照存入至磁盘中,Redis重新启动后会将快照以二进制的形式给加载进入内存中,其中rdbSaverdbLoad函数至关重要

image

什么时候会触发RDB?

  • Redis停机时会触发
  • 使用命令 save m n
  • 主从同步执行全量复制
  • debug reload命令重新加载Redis

保存

在RDB文件期间,主进程会被阻塞,直至保存完成,其中也分为了两种不同的保存方式SAVEBGSAVE

def SAVE():
 	rdbSave()
 
def BGSAVE():
     pid = fork()
     if pid == 0:
    	 # 子进程保存 RDB
     	rdbSave()
     elif pid > 0:
     	handle_request()
 	 else:
         # pid ==-1
         # 处理 fork 错误
        handle_fork_error()
  • SAVE: 该方法会阻塞Redis主进程,此时不会响应用户请求,直至保存完成位置

image

  • **BGSAVE:**该方法不会阻塞Redis主进程,主线程会查看是否已经fork了一个子线程,如果fork则返回,否则fork一个子进程采用CopyOnWrite机制将当前的快照存入磁盘中

image

SAVE,BGSAVE,BGREWRITEAOF能同时执行吗

  • 对于SAVE来说:

    由于SAVE是阻塞当前主进程的,所以此时此刻不管是用户命令还是BGSAVEBGREWRITEAOF都无法执行,在执行SAVE指令前会检查BGSAVE是否在执行,如果在执行则不能进行SAVE

  • 对于BGSAVE来说:

    **BGSAVE与BGSAVE:**通过上述代码我们可以知道,当BGSAVE正在执行时,会检查子进程是否fork如果fork了子进程则直接返回,所以是不能同时执行的

    **BGSAVE与BGREWRITEAOF:**BGSAVE正在执行时,BGREWRITEAOF会延迟收到指令直至BGSAVE执行完毕。如果BGREWRITEAOF正在执行,BGSAVE会直接返回报错,无法同时执行。

载入

Redis服务器启动时,就会进行rdbLoad函数,此时在载入期间每载入1000条数据就会处理一次当前的用户命令,当然这里的用户命令只能时订阅与发布功能相关的,其他的命令都会统一拒绝。

在载入的时候会优先选择AOF,如果没有设置AOF才会使用RDB

RDB文件结构

一个RDB的文件结构如下所示:

image

  • **REDIS:**该字符标识着RDB文件的开始,相当于魔数
  • RDB-VERSION(四字节): 记录了当前文件RDB的版本号,读取的时候要使用对于版本号的方法读取
  • DB-DATA: 该部分会在RDB文件中出现多次,保存着一个服务器上非空数据库的所有数据
  • SELECT-DB: 代表着该键值对所属的数据库号码,读入RDB文件时,会根据该号码不断切换数据库
  • KEY-VALUE-PAIRS: 代表着一个键值对的数据,每个键值对的数据会用以下结构来进行保存

image

OPTIONAL-EXPIRE-TIME: 这个代表着当前的键值对过期时间,如果没有则为null

TYPE-OF-VALUE: 代表着该键值对以什么样的类型进行存储,会根据不同的类型来进行VALUE的读取(这个地方内容较多,暂不介绍)

KEY: 存储着当前的键

VALUE: 存储着当前键保存的值

  • EOF: 标志数据库内容的结尾,并不是文件的末尾
  • CHECK-SUM: 文件内容校验和,读取时会对其进行文件内容的校验,如果为0则代表关闭了校验和功能

AOF

AOF以协议文本的方式,将所有对数据库写入的命令记录至AOF文件中,以此达到记录数据库状态的目的

image

AOF运行阶段

同步命令至AOF文件分为三个阶段:

命令传播: 将当前的Redis执行完的命令,以命令请求,命令参数,命令参数个数的形式传输给AOF程序

image

缓存追加: 将命令数据接收,并转换为网络通讯的协议方式,将内容追加至AOF缓存当中。

文件写入和保存: 将缓存的内容根据设定的AOF条件写入至AOF文件末尾,此时会调用fsync函数或者fdatasync函数来将写入内容保存至磁盘中

此时会调用aof.c/flushAppendOnlyFile函数来执行以下两个工作:

WRITE(主进程阻塞):根据写入条件,将当前aofBuf的内容写入至AOF文件末尾,这个是写入至文件缓冲区的,写入之后直接返回,如果此时发生宕机,此时写入的内容将丢失

SAVE(主进程看情况阻塞) :根据保存条件,将当前的AOF文件缓存内容保存至磁盘中。采用fsync或fdatasync。

fysnc和fdatasync

参考文献

**fsync:**他会刷新文件的所有修改的核心数据包括文件关联的元数据,再刷新至磁盘中时,他会一直阻塞直至刷新完成

**fdatasync:**他和fsync类似,但是他不会刷新所有的文件元数据,会根据需要来进行刷新

AOF保存模式

Redis 目前支持三种 AOF 保存模式,它们分别是:

AOF_FSYNC_NO :不保存

在不保存的情况下,整个Redis执行期间WRITE会被执行但是不会执行SAVE命令,只有以下几种可能会执行SAVE命令

  • Redis被关闭
  • AOF被关闭
  • 系统的写缓存被刷

这三种情况下的SAVE都会导致主进程阻塞

AOF_FSYNC_EVERYSEC :每一秒钟保存一次

SAVE在原则上会一秒钟执行一次,且这个SAVE是由fork出来的子进程进行执行的,但是值得注意的是这个是原则上面的一秒钟,它是否是每次一秒调用和当前Redis所处的状态有关。

image
  • 子线程正在执行SAVE:

​ 如果执行SAVE时间小于2s:无需进行额外的write和save,程序执行返回

​ 如果执行SAVE时间超过2s:程序执行追加write,但不执行新的save。此时的write必须等待save执行完毕才能进行,所以主线程也会阻塞

  • 子线程没有执行SAVE:

​ 如果上次执行SAVE距今不超过1s:程序执行write但不执行save

​ 如果上次执行SAVE时间距今超过1s:程序执行write和save

所以我们如果在上图的情况1宕机,那此时只会损失小于2s的数据,但是如果在情况发生宕机,此时write已经有2s没写入文件缓存并刷入磁盘,就会有2s的数据损失。所以说AOF_FSYNC_EVERTSEC只损失1s的数据是不准确的

AOF_FSYNC_ALWAYS :每执行一个命令保存一次

每次执行完一个命令都会执行一次wirte指令和save指令,但是save是Redis主进程执行的所以主进程会阻塞

三种保存模式的对比图

image

image

AOF文件读取与数据还原

AOF文件内容如下

*2
 $6
 SELECT
 $1
 0
 *3
 $3
 SET
 $3
 key
 $5
 value
 *8
 $5
 RPUSH
 $4
 list
 $1
 1
 $1
 2
 $1
 3
 $1
 4
 $1
 5
 $1

我们可以看到在AOF文件内容中有一个SELECT 0 指令,该指令是为AOF文件指定要还原的数据库。

AOF文件的数据还原步骤如下:

① 开启一个伪客户端(fake cilent)

② 读取AOF的文件内容,并将其处理为命令,命令参数,参数个数该形式

③ 使伪客户端执行这些命令,直至所有命令执行完毕

这三步结束后,便会将AOF文件中的内容全部还原成数据库数据。在加载和还原期间只有订阅和发布功能能够使用,其他的都不能使用。

def READ_AND_LOAD_AOF():
 	# 打开并读取 AOF 文件
    file = open(aof_file_name)
 	while file.is_not_reach_eof():
         # 读入一条协议文本格式的 Redis 命令
        cmd_in_text = file.read_next_command_in_protocol_format()
         # 根据文本命令,查找命令函数,并创建参数和参数个数等对象
        cmd, argv, argc = text_to_command(cmd_in_text)
 		# 执行命令
		execRedisCommand(cmd, argv, argc)
 	# 关闭文件
	file.close()

AOF重写

对于一个Redis服务器来说,可能会接收几十万上千万的指令请求,如果此时将这些请求全部存入AOF文件,将会导致AOF文件不断庞大,对Redis和系统造成影响,于是为了将AOF文件进行压缩,便设计了AOF重写方法:AOF文件并不一定要写入所有的客户端指令只要保证前后状态一致即可,创建一个新的AOF文件替代原本的AOF文件,新AOF文件和原有的AOF文件对于数据库状态完全一样

实现原理:

对于下列命令集合我们可以发现,我们一开始创建了一个list[1,2,3,4],然后经过三次操作将其变为了list[1,2,3],那其实这四段命令我们可以直接压缩成一行也就是RPUSH list 1 2 3,这样即使的结果和前面的四次操作完全一致。

RPUSH list 1 2 3 4	 // [1, 2, 3, 4]
RPOP list			// [1, 2, 3]
LPOP list			// [2, 3]
LPUSH list 1		// [1, 2, 3]
def AOF_REWRITE(tmp_tile_name):
 	f = create(tmp_tile_name)
 	# 遍历所有数据库
    for db in redisServer.db:
         # 如果数据库为空,那么跳过这个数据库
        if db.is_empty(): continue
         # 写入 SELECT 命令,用于切换数据库
        f.write_command("SELECT " + db.number)
     	# 遍历所有键
        for key in db:
     		# 如果键带有过期时间,并且已经过期,那么跳过这个键
            if key.have_expire_time() and key.is_expired(): continue
    	    if key.type == String:
         		# 用 SET key value 命令来保存字符串键
    			value = get_value_from_string(key)
     			f.write_command("SET " + key + value)
     		elif key.type == List:
                 # 用 RPUSH key item1 item2 ... itemN 命令来保存列表键
                 item1, item2, ..., itemN = get_item_from_list(key)
     			f.write_command("RPUSH " + key + item1 + item2 + ... + itemN)
     		elif key.type == Set:
                 # 用 SADD key member1 member2 ... memberN 命令来保存集合键
                member1, member2, ..., memberN = get_member_from_set(key)
                f.write_command("SADD " + key + member1 + member2 + ... + memberN)
     		elif key.type == Hash:
                 # 用 HMSET key field1 value1 field2 value2 ... fieldN valueN 命令来保存哈希键
                field1, value1, field2, value2, ..., fieldN, valueN =\
                get_field_and_value_from_hash(key)
                f.write_command("HMSET " + key + field1 + value1 + field2 + value2 +\
                ... + fieldN + valueN)
     		elif key.type == SortedSet:
                 # 用 ZADD key score1 member1 score2 member2 ... scoreN memberN
                 # 命令来保存有序集键
                 score1, member1, score2, member2, ..., scoreN, memberN = \
                 get_score_and_member_from_sorted_set(key)
                 f.write_command("ZADD " + key + score1 + member1 + score2 + member2 +\
                 ... + scoreN + memberN)
             else:
                 raise_type_error()
                 # 如果键带有过期时间,那么用 EXPIREAT key time 命令来保存键的过期时间
    		if key.have_expire_time():
     			f.write_command("EXPIREAT " + key + key.expire_time_in_unix_timestamp())
         # 关闭文件
        f.close()

从上面的代码我们可以总结出以下步骤:

  • 遍历数据库,如果数据库为空则跳过,否则进入进行key遍历
  • 对所有的key进行遍历,如果key过期了则跳过,否则直接根据类型获取key的值,然后通过set的方式将其写入AOF文件中
  • 如果key有过期时间则给其赋予过期时间
  • 关闭文件写入
后台AOF重写

通过上面的AOF重写我们可以得知AOF重写是阻塞的,Redis也为AOF重写fork了一个子进程进行重写的处理。

image

① 执行BGREWRITEAOF指令,父进程fork出一个子进程来进行AOF重写操作

② 同时创建aof_rewrite_buf来缓存在重写过程中,执行的新的命令,父进程会将执行的命令同时放入aof_rewrite_buf和aof_buf中,保证不管是重写失败还是重写过程中都不会发生丢失数据的情况。

③ 子进程根据aof_rewrite_buf将重写后的指令写入新的AOF文件中

④ 当前重写全部执行完成后向父进程发送一个通知

⑤ 父进程将新的AOF文件与旧的AOF文件替换,完成重写

重写的自动触发条件:

重写可以通过手动命令bgrewriteaof进行,也可以自动进行不过要符合以下条件

  • 没有BGSAVE在执行
  • 没有SAVE在执行
  • 没有BGREWRITEAOF在执行
  • 当前AOF文件大小大于aof_rewrite_min_size(重写触发最小值 默认1mb)
  • 比较当前AOF文件和最后一次AOF文件重写的大小之间的比例是否超过一倍(比如当前AOF文件是2MB,重写时的文件是1MB,此时就超过一倍了)

符合以上条件则会进行自动的AOF重写。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/534019.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c++ 字符串与STL用法

文章目录 StringVector构造增加删除大小清空排序 map定义添加数据遍历数据查找清空删除长度判断空交换排序常用用法 String Vector vector是动态扩充的数组 构造 ​ vector()​:创建一个空vectorvector(int nSize)​:创建一个vector,元素个数为nSize​vector(int nSize,co…

【MATLAB】基于Wi-Fi指纹匹配的室内定位-仿真获取WiFi RSSI数据(附代码)

基于Wi-Fi指纹匹配的室内定位-仿真获取WiFi RSSI数据 WiFi指纹匹配是室内定位最为基础和常见的研究,但是WiFi指纹的采集可以称得上是labor-intensive和time-consuming。现在,给大家分享一下我们课题组之前在做WiFi指纹定位时的基于射线跟踪技术仿真WiFi…

Josephus排列:组合数学与跨学科应用

Josephus排列:组合数学与跨学科应用 一、背景二、定义和历史三、问题的形式化描述四、解决方案4.1 n2,任意m4.2 m1,任意n4.3 n为奇数,m为偶数4.4 n和m都是奇数 五、红黑树简介六、Josephus排列问题描述七、使用红黑树解决Josephus…

设计模式之备忘录模式(上)

备忘录模式 1)概述 1.定义 在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,可以在以后将对象恢复到原先保存的状态。 2.作用 备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便…

展厅设计方案需要考虑哪些因素环节

1、整体展厅规划 整体规划是展厅搭建中需要优先考虑的环节,必须为搭建成本和效果提供坚实的基础。需确定整体展厅面积、展厅的使用目的、产品布局以及进入展厅的路线、确定展厅的整体框架之后,还需考虑展品种类、产生的呈现效果,这些都将影响…

el-table动态合并列

需要合并列,并且不确定需要合并多少列的,可以参照如下代码 首先需要再el-table上传入span-method方法 arraySpan({ row, column, rowIndex, columnIndex }){if (row.groupName 汇总 && columnIndex 0) {return [0,0]} else if (row.groupName…

迷宫 — — 蓝桥杯(动态规划)

迷宫 题目: 输入样例: 3 1 1 1 2 3 4 5 6 7 8 9 2 2 1 3 1 R输出样例: 21思路: 题目大意:给定一个n x m的平面网格,并且每一个格子都有一定的代价,并且设有障碍物和陷阱,障碍物的意…

win11 连接海康摄像头 ONVif协议

目录 Win 11 通过脚本打开自带的IE浏览器访问海康摄像头 海康摄像头设置支持onvif协议 安装onvif协议 onvif协议示例代码 Win 11 通过脚本打开自带的IE浏览器访问海康摄像头 第一步、桌面右键新建一个 txt 的文档 第二步、打开文档并且复制粘贴下面代码 CreateObject(&…

【科研】搜索文献的网站

文章目录 paperswithcode【最新论文,代码】huggingface【大语言模型,最新论文】dblp【关键词搜索】arxiv【最新文章】semanticscholar【相关引用查询】connectedpapers【相关引用查询】github【工程,代码,论文开源代码】 paperswi…

OV证书为什么更可信

在网络安全领域,SSL/TLS证书扮演着至关重要的角色,其中组织验证(Organization Validation,简称OV)证书以其深度验证机制和高度可信性脱颖而出。 OV证书为何更值得信赖,关键在于其严格的验证流程。 首先&am…

金三银四面试题(十九):MySQL中的锁

在MySQL中,锁是非常重要的,特别是在多用户并发访问数据库的环境中,因此也是面试中常问的话题。 请说说数据库的锁? 关于MySQL 的锁机制,可能会问很多问题,不过这也得看面试官在这方面的知识储备。 MySQL …

东方博宜 1169. 编程输入10个正整数,然后自动按从大到小的顺序输出

东方博宜 1169. 编程输入10个正整数&#xff0c;然后自动按从大到小的顺序输出 学了sort函数的新用法。 从小到大排列 sort(a , an ) 从大到小排列 sort(a , an , greater() ) #include<iostream> #include<algorithm> using namespace std; int main() {int a[…

瑞_23种设计模式_访问者模式

文章目录 1 访问者模式&#xff08;Visitor Pattern&#xff09;1.1 介绍1.2 概述1.3 访问者模式的结构1.4 访问者模式的优缺点1.5 访问者模式的使用场景 2 案例一2.1 需求2.2 代码实现 3 案例二3.1 需求3.2 代码实现 4 拓展——双分派4.1 分派4.2 动态分派&#xff08;多态&am…

新型[datahelper@onionmail.org].datah 勒索病毒来袭:如何筑起安全防线?

在数字化时代&#xff0c;网络安全问题日益凸显&#xff0c;其中勒索病毒成为了一种非常严重的威胁。[datahelperonionmail.org].datah勒索病毒就是其中的佼佼者&#xff0c;它以其复杂的加密手段和恶劣的勒索行为&#xff0c;给用户带来了巨大的损失。本文将从病毒的运行机制、…

systemctl start docker报错(code=exited, status=1/FAILURE)

运行systemctl start docker报错内容如下: 输入systemctl status docker.service显示以下内容&#xff1a; 本次启动不起来与docker服务无关 具体解决问题是修改 /etc/docker/daemon.json&#xff0c;vim /etc/docker/daemon.json # 添加如下内容 {"registry-mirrors&qu…

Win10安装sqlplus遇到报错的解决办法

1.下载安装sqlplus.exe的错误解决过程 最近有用到sqlplus连接Oracle数据库执行自动化脚本&#xff0c;Orcle服务器版本是11.2.0.1。在Navicat工具上通过如下语句查询到的版本信息截图如图1所示&#xff1a; SELECT * FROM v$version; 图1 Oracle服务器版本信息 其中“Oracle Da…

图像分割-RSPrompter

文章目录 前言1. 自动化提示器1.1 多尺度特征增强器1.2 RSPrompterAnchor-based PrompterQuery-based Prompter 2. SAM的扩展3. 结果WHU数据集NWPU数据集SSDD数据集 前言 《RSPrompter: Learning to prompt for remote sensing instance segmentation based on visual foundati…

面试算法-172-对称二叉树

题目 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 解 class Solution {public boolean isSymmetric(TreeNode root) {return isSymm(root.left,root.right);}public b…

Python学习笔记——heapq

堆排序 思路 堆排序思路是&#xff1a; 将数组以二叉树的形式分析&#xff0c;令根节点索引值为0&#xff0c;索引值为index的节点&#xff0c;子节点索引值分别为index*21、index*22&#xff1b;对二叉树进行维护&#xff0c;使得每个非叶子节点的值&#xff0c;都大于或者…

Redis持久化策略详解

文章目录 前言一、RDB(Redis Database)1.1 概念1.2 触发方式 二、AOF(Append Only File)2.1 概念2.2 AOF持久化策略2.3 AOF文件重写2.4 触发条件2.5 AOF配置说明 三、混合持久化3.1 概念3.2 开启混合持久化3.3 加载流程3.4 混合持久化优劣势 四、总结4.1 RDB和AOF各自有什么优缺…