Redis 中的 RDB 和 AOF 持久化机制

一、Redis 持久化简介

Redis 的持久化功能是区别于 Memcached 显著特性,数据持久化可以保证系统在发生宕机和重启后数据不会丢失,对于 redis 这种存储在内存中的数据库显得尤为重要。 在 Redis 4.0 以前数据持久化的方式主要有两种

  • RDB(Redis DataBase)快照方式: 它是将某一时刻的内存数据以二进制的方式写入磁盘,默认保存文件为 dump.rdb

  • AOF(Append Only File)文件追加方式:它是指将所有的操作命令,以文本的形式追加到文件中。默认保存文件是 appendonly.aof

二、RDB 持久化机制

RDB 是将内存中的数据在某一时刻的状态记录下来以二进制的方式存储到磁盘中,通过生成一个经过压缩的二进制文件来实现数据库状态的复原。默认是 dump.rdb 文件,它的优点是以二进制存储,占用空间更小,数据存储更紧凑。

因为 RDB 文件是保存在磁盘中,哪怕 Redis 服务器宕机,Redis 服务器就可以通过该文件来还原数据库状态。

2.1 RDB 触发方式

触发RDB持久化既可以通过手动执行,也可以根据服务器配置选项定期执行。主要分为手动触发和自动触发两种方式。

2.1.1 手动触发

手动触发主要是通过save 和 bgsave 两个命令来生成RDB 文件

  1. save 命令
  • **会阻塞当前 Redis 服务器,然后直到 RDB 文件创建完毕为止 **。对于内存较大的实例会造成长时间阻塞。
  1. bgsave 命令
  • 该命令会派生fork出一个子进程,由子进程负责创建RDB文件,服务器(父线程)继续处理命令请求。RDB 持久化过程由子进程负责,完成后自动结束,阻塞阶段只发生在 fork 阶段,阻塞时间较短。所以大多数情况使用bgsave 命令生成RDB文件

2.1.2 自动触发

自动触发主要是通过 redis.conf 配置文件来完成:

# save <seconds> <changes> 周期性执行RDB持久化条件的设置格式
# 默认配置
save 900 1
save 300 10
save 60 10000

# 文件名称
dbfilename dump.rdb

# 文件保存路径
dir /home/work/app/redis/data/

# 如果持久化出错,主进程是否停止写入
stop-writes-on-bgsave-error yes

# 是否压缩
rdbcompression yes

# 导入时是否检查,这里是使用CRC64算法来进行数据校验,会增加性能消耗
rdbchecksum yes

还有其他情况也可能会导致自动触发:

  • 主从复制时,从节点要从主节点进行全量复制时也会触发 bgsave 操作,生成当时的快照 RDB 文件发送到从节点
  • 执行debug reload 命令重新加载 redis 时也会触发 bgsave 操作,生成 RDB 文件
  • 默认情况下执行 shutdown 命令时,若没有开启 AOF 持久化,也会触发 bgsave 操作,生成 RDB 文件

2.2 RDB 实战问题

1.在某一时刻给数据量较大内存进行快照,此时服务器也会收到数据写请求,如何保证数据一致性?

数据量较大时进行快照,用时相对会比较长。如果服务器在这个期间收到写请求,那么就不能保证快照的完整性。那么Redis 是如何做的?

Redis 使用的是操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。

如下图,针对 bgsave 命令触发 RDB 机制来说,如果主线程要修改一块数据(图中的键值对 C),这块数据就会被复制一份,生成该数据的副本(键值对C’)。此时中线程在该副本上进行修改,bgsave 子线程继续把原来的数据(键值对 C)写入 RDB 文件中。

这样既保证了RDB 文件的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。

2.在进行 RDB 快照的过程中,发生服务崩溃了怎么办?

数据没有全部写入磁盘前,该次快照操作都是失败的。将以这次快照操作前的完整 RDB 快照文件作为恢复内存的数据。也就是此次快照操作过程不能影响上一次的备份数据。实际上在操作时 Redis 服务会在磁盘上创建一个临时文件来进行数据操作,待操作成功后才会用这个临时文件替换上一次的备份数据。

3.可以每秒做一次快照吗?

频繁的执行全量快照,能够带来更快的恢复速度。但同时也会带来大量的开销,给磁盘带来压力,多个快照竞争有限的磁盘带宽,容易造成恶性循环。此外, bgsave 子进程需要通过 fork 操作从主线程创建出来。如果频繁的fork 出 bgsave 子进程就会阻塞主线程。因此 Redis 在有一个 bgsave 运行时,就不会再启动第二个 bgsave 子进程。

这里就可以采用增量快照的方法,就是在全量快照后,后续的快照只对修改后的数据进行快照,避免对数据重复的那部分进行快照带来的消耗,这样更加高效。这里就需要对修改的文件数据进行记录,如下图所示:

但是,我们在增量快照时,记录所修改的数据信息也是一部分的开销,在大量数据修改时的记录数据,其内存开销也不少。所以对于做快照的频率,不能太高也不能太低。那么有什么解决方法既能够让 RDB 快速恢复,又能以较小的开销实现少丢数据呢?

Redis 4.0 提出了 混合使用 AOF 日志和内存快照的方法可以很好的解决上面的场景。

三、AOF 持久化机制

AOF (Append Only File)日志和大多数的数据库写入日志的方式不同,采用的是写后日志。(比如 MySQL 是通过写前日志(Write Ahead Log, WAL)和两阶段提交来实现数据和逻辑的一致性)

写后日志的意思是 Redis 先将数据写入内存,然后再记录日志,如下图:

采用后写日志有这样的好处:

  • 避免额外的检查开销:比如在我们输入命令时可能会出错,如果先记录日志再执行命令,日志中可能记录错误的命令,在利用日志恢复数据时可能会出错。那先命令,后记录日志就可以当命令执行成功再记录,避免出现记录错误命令的情况
  • 不会阻塞当前写操作:因为它是在命令执行后才记录日志,不会执行当前的写操作

但是在写回磁盘时也会产生风险:

  • 在命令执行和写日志中间出现宕机,执行命令数据会发生丢失
  • 虽然避免当前命令的阻塞,但是可能会给下一个操作带来阻塞风险。磁盘写压力大,会导致写盘很慢,进而导致后续操作无法执行。

这两个风险是 AOF 写回磁盘时机不当造成的,对于这两个风险,AOF 有三种写回策略可以避免。也就是在文件写入同步时发生。

3.1 AOF 持久化的实现

AOF 持久化功能的实现可以分为命令追加(append)、文件写入(write)和文件同步(sync)三个步骤。

3.1.1 命令追加

当 AOF 持久化功能处于打开状态时,服务器在执行完一个写命令之后,就会以协议格式将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的末尾中,这个 aof_buf 是以 sds 对象形式存储。

struct redisServer {
    ...
    // AOF 缓冲区
    sds aof_buf;
    ...
};

以Redis 服务端收到 set testkey testvalue 命令后的记录为例,查看 AOF 日志的运行原理。

在服务器执行 set 命令后,会将以下协议内容追加到 aof_buf 缓冲区的末尾:

*3\r\n$3\r\nSET\r\n$7\r\ntestkey\r\n$9\r\ntestvalue\r\n

  • *3 :表示当前命令有三个部分,每个部分都是由$+数字开头,后面紧跟具体的命令、键或值
  • \r\n:是换行符,具体如上图所示
  • $+数字:数字表示这部分的命令、键或值一共有多少个字节,比如 $3 set 表示这部分有三个字节的命令,也就是 set 命令
3.1.2 文件写入和同步

Redis 的服务器进程就是一个事件循环(loop),在处理文件事件时可能会执行写命令,所以在服务器每次结束一个事件循环之前,它会调用 flushAppendOnlyFile 函数,考虑是否需要将 aof_buf 缓冲区中的内容写入到AOF 文件中去。

def eventLoop():
	while True:
		//处理文件事件,接收命令请求以及发送命令回复
		//处理命令请求时可能会有新内容被追加到 aof_buf缓冲区中
		processFileEvents();
		//处理时间事件
		processTimeEvents();
		//考虑是否要将 aof_buf中的内容写入和保存到 AOF文件里面
		flushAppendOnlyFile();

flushAppendOnlyFile函数的行为有服务器配置的 appendfsync 选项的值来决定。其值主要有三种:

3.1.3 AOF 文件的载入与数据还原

在保存了 AOF 文件后,服务器只需要重新执行 AOF 文件中的写命令后,就可以还原服务器关闭之前的数据库状态,其具体步骤如下:

  1. 创建一个不带网络连接的伪客户端(fake client)
  2. 从 AOF 文件中分析并读取一条写命令
  3. 使用 fake client 执行被读出的 写命令
  4. 重复执行步骤2和3,直到 AOF 命令中所有写命令都被处理完毕

3.2 AOF 重写机制

因为 AOF 是通过保存被执行的写命令来完成 Redis 持久化的,所以随着 Redis 的运行时间越长,AOF 文件中存储的日志内容会越来越多。会给 Redis 服务器,带来很大的影响。因此 Redis 提供了 AOF 文件重写功能解决这一痛点。

3.2.1 重写机制是如何减少 AOF 文件大小的?

重写机制具有“多变一”的功能,也就是对旧日志文件中的多条命令,在重写后的新日志变成了一条命令。如下图所示:

3.2.2 重写会阻塞吗?

AOF 重写过程是由后台子进程 bgrewriteaof 来完成,重写过程是一个拷贝,两处日志:

  • 一个拷贝:是指每次执行重写时,主线程 fork 出后台的 bgrewriteaof 子进程。此时,fork 会把主线程的内存拷贝一份给 bgrewriteaof。所以这个子线程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。

  • 两处日志:

    • 第一处日志指的是正在使用的 AOF 日志,Redis 会把这个操作写到它的缓冲区。即使宕机了,这个 AOF 日志的操作仍然是齐全的可以用于恢复。

    • 第二处日志指的是新 AOF 重写日志,这个操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作

所以说在每次 AOF 重写时,Redis 会先执行一份内存拷贝,用于重写;然后使用两个日志保证在重写过程中新写入的数据不会丢失。同时因为 bgrewriteaof 子进程的出现,这个过程并不会阻塞主线程。

3.2.3 AOF 日志何时会进行重写?

有两个配置项可以控制 AOF 重写的触发:

  • auto-aof-rewrite-min-size: 表示运行 AOF 重写时文件的最小大小,默认是 64MB
  • auto-aof-rewrite-percentage: 这个值是当前 aof 文件和上一次重写后 aof 文件差值,再除以上一次重写 aof 文件大小。

四、RDB 和 AOF 区别与联系

4.1 RDB 和 AOF 混合机制

在介绍 RDB 机制时,我们了解到是否有一种方法既能利用 RDB 的快速恢复,又能以较小的开销做到尽量少丢数据?也就是混合使用 RDB 和 AOF 两种机制的方法:就是让 RDB 快照以一定频率执行,在两次快照之间使用 AOF 日志记录这期间的所有命令操作。

如上图所示,第一次全量快照后,在T1 和 T2 时刻修改用 AOF 日志记录,等到第二次全量快照时,可以清空 AOF 日志。因为此时的修改都已经记录到快照中,恢复时也就不再使用日志了。

4.2 RDB 和 AOF 优缺点

RDB
  • 默认文件是 dump.rdb。具备更快速的数据重启恢复能力,以二进制存储占用更小的磁盘空间,但是有数据丢失的风险
AOF
  • 默认保存文件是 appendonly.aof。存储频率更高,存储信息更容易懂,缺点是占用空间大,重启之后的数据恢复速度较慢。

所以 RDB 和 AOF 混合机制综合了上述两种机制的优缺点。

4.3 RDB 和 AOF 文件的载入和还原

AOF 的载入和还原过程在 AOF 小节中有提到。现在谈谈同时存在两者的情况,服务器如何用哪个文件来还原数据库状态:

  • 如果服务器开启了 AOF 持久化功能,优先使用 AOF 文件,因为AOF 更新频率通常要比 RDB 文件要高
  • 只有当 AOF 持久化功能关闭时,服务器才会使用 RDB 文件来还原数据状态

参考资料:

《redis 设计与实现》

https://baijiahao.baidu.com/s?id=1654694618189745916&wfr=spider&for=pc

https://time.geekbang.org/column/article/271839

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

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

相关文章

初学gitrepo的种种

经过各种折腾之后&#xff0c;发现git其实还是很简单的&#xff1b; 首先你需要两台机器&#xff0c;一台作为服务器&#xff0c;一台作为开发机器&#xff0c;开发机器从服务器上拉取代码。 目 目录 git建仓 开发机器拉取代码 初始化仓代码 repo管理 repo工具的下载 …

Ansible的脚本---Playbook剧本编写

playbook的组成部分 1、 tasks&#xff1a;任务 在目标主机上需要执行的操作。使用模块定义这些操作。每个任务都是一个模块的调用。 2、 variables&#xff1a;变量 用于存储和传递数据。类似于shell脚本中的变量。变量可以自定义。可以在playbook当中定义为全局变量&…

Go 语言实战:掌握正则表达式的应用与技巧

Go 语言实战&#xff1a;掌握正则表达式的应用与技巧 1. 引言2. 正则表达式基础2.1 基本概念2.2 常见元素2.3 基本示例 3. Go语言中的正则表达式库3.1 引入regexp包3.2 编译正则表达式3.3 使用正则表达式3.4 示例代码 4. 常用正则表达式函数及使用示例4.1 MatchString4.2 FindS…

配置自定义RedisTemplate 解决redis序列化java8 LocalDateTime

目录 配置自定义RedisTemplate 引入依赖 配置连接redis 编写测试类 出现问题 配置序列化 解决redis序列化java8 LocalDateTime 问题背景 问题描述 问题分析 解决方案一&#xff08;全局&#xff09; 解决方案二&#xff08;单个字段&#xff09; 配置自定义RedisTe…

在GitHub找开源项目

在 GitHub 的搜索框里&#xff1a; 使用搜索关键词可以在 GitHub 上快速的找你需要的开源项目&#xff1a; 限制搜索范围 通过 in 关键词 (大小写不敏感) 限制搜索范围&#xff1a; 公式搜索范围in:name xxx项目名包含xxxin:description xxx项目描述包含xxxin:readme xxx项目…

thymeleaf的个人总结

thymeleaf的java代码的用法 <!-- 静态页面模版引擎的依赖 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> 我们在这里要配置一个具体的模版…

CVE-2023-37582 Apache RocketMQ NameServer远程代码执行漏洞

上集回顾 CVE-2023-33246 RocketMQ RCE漏洞 影响版本 Apache RocketMQ NameServer 5.0.0 &#xff5e; 5.1.1 Apache RocketMQ NameServer 4.0.0 &#xff5e; 4.9.6 参考 https://xz.aliyun.com/t/12691 环境搭建 拉取镜像 docker pull apache/rocketmq:4.9.6 docker …

Bugku- misc-插画-WP

下载得到一个zip&#xff0c;用WinRAR打开时发现有注释 注释&#xff1a; RnJlZV9GaWxlX0NhbW91ZmxhZ2UsIOmimOebruWlveWDjaYraMuumHjeimgeeahOagtWtkC4u 明显是base64&#xff0c;解码得到&#xff1a;Free_File_Camouflage, 题目好像是挺重要的样子… 百度发现这是一款隐写…

微电网优化(Matlab复现)— 微电网两阶段鲁棒优化经济调度方法_刘一欣

论文链接&#xff1a;微电网两阶段鲁棒优化经济调度方法 - 中国知网 代码链接&#xff1a;https://m.tb.cn/h.5Mg7fCo?tkhnpmWgZiv2R 复现效果&#xff1a; 运行环境&#xff1a;Matlab 2020bCplexyalmip 1 微电网结构 图 1 所示为典型的微电网结构&#xff0c;由可控分布式…

uniapp实现豆瓣电影微信小程序(附源码)

演示 运行 基于本地代理1 npm run dev:proxy1基于本地代理2 npm run dev:proxy2基于nginx 代理 npm run dev:nginx目录结构 |__ douban # 本地代理|__ app.js # 方式 1|__ proxy.js …

基于vue-cli快速发布vue npm 包

一、编写组件 1. 初始化项目并运行 vue create vue-digital-countnpm run serve2. 组件封装 新建package文件夹 ​ 因为我们可能会封装多个组件&#xff0c;所以在src下面新建一个package文件夹用来存放所有需要上传的组件。 ​ 当然&#xff0c;如果只有一个组件&#xff…

SQL面试题挑战01:打折日期交叉问题

目录 问题&#xff1a;SQL解答&#xff1a;第一种方式&#xff1a;第二种方式&#xff1a; 问题&#xff1a; 如下为某平台的商品促销数据&#xff0c;字段含义分别为品牌名称、打折开始日期、打折结束日期&#xff0c;现在要计算每个品牌的打折销售天数&#xff08;注意其中的…

【yolov8系列】 yolov8 目标检测的模型剪枝

前言 最近在实现yolov8的剪枝&#xff0c;所以有找相关的工作作为参考&#xff0c;用以完成该项工作。 先细读了 Torch-Pruning&#xff0c;个人简单记录了下 【剪枝】torch-pruning的基本使用&#xff0c;有框架完成的对网络所有结构都自适应剪枝是最佳的&#xff0c;但这里没…

Redis 系统性总结看这一篇就够了

Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务&#xff0c;是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 之前在公司一直忙于使用&#xff0c;很少做系统性的总结&a…

工具系列:PyCaret介绍_单变量时间序列代码示例

&#x1f44b; 工具系列&#xff1a;PyCaret介绍_单变量时间序列代码示例 PyCaret是一个开源的、低代码的Python机器学习库&#xff0c;可以自动化机器学习工作流程。它是一个端到端的机器学习和模型管理工具&#xff0c;可以大大加快实验周期&#xff0c;提高工作效率。 与其…

用C#也能做机器学习?

前言✨ 说到机器学习&#xff0c;大家可能都不陌生&#xff0c;但是用C#来做机器学习&#xff0c;可能很多人还第一次听说。其实在C#中基于ML.NET也是可以做机器学习的&#xff0c;这种方式比较适合.NET程序员在项目中集成机器学习模型&#xff0c;不太适合专门学习机器学习&a…

从Gitee克隆项目、启动方法

从gitee克隆VUE项目到本地后&#xff0c;不能直接运行&#xff0c;需要进行npm install安装node_modules文件夹里面的内容&#xff0c;因为在git上传的时候&#xff0c;一般都会过滤到node_modules中的依赖文件。 安装依赖以后&#xff0c;启动通过npm run serve启动项目出错。…

迪文屏开发保姆级教程——页面键盘

迪文屏页面键盘保姆级教程。 本篇文章主要介绍了在DGBUS平台上使用页面键盘的步骤。 迪文屏官方开发指南PDF&#xff1a;&#xff08;不方便下载的私聊我发给你&#xff09; https://download.csdn.net/download/qq_21370051/88647174?spm1001.2014.3001.5503https://downloa…

一篇文章带你搞定ARCHPR的下载和使用

除了bp这款爆破神器之外&#xff0c;另外还有一款ARCHPR的字典爆破神器&#xff0c;好处是很方便&#xff0c;而且爆破速度贼快 链接&#xff1a;https://pan.baidu.com/s/1-Ewx2JpZ-o5PunlfkRupYg 提取码&#xff1a;sg51 这里直接给大家安排了&#xff0c;自取就好 攻击的…

常用的电源芯片有哪些?怎么分类

科技的发展也带动了电源的发展&#xff0c;因此需要更多的电源管理芯片。说到电源管理芯片&#xff0c;作为工程师最熟悉的芯片之一。所谓电源管理芯片&#xff0c;就是负责电子设备系统中电能的转换、分配、检测等电能管理的芯片。主要负责识别CPU电源范围&#xff0c;产生相应…