Redis持久化AOF和RDB,学习总结篇!

news2024/11/26 17:47:51

大家好,我是三叔,很高兴这期又和大家见面了,一个奋斗在互联网的打工人。

前面笔者写了一篇关于Redis 数据结构和数据类型的博客总结,这篇博客总结一下关于 Redis 持久化。部分图片来自作者:小林哥,小林哥yyds ~

序言

Redis 持久化主要使用过 AOF(追加文件) 和 RDB(快照) 来保证 Redis 持久化的,这样是为了实现 Redis 的高可用,避免在服务宕机之后,数据无法恢复的问题。Redis 默认是开启 RDB 快照的,AOF 需要在 Redis 的配置文件 redis.conf 中进行修改。

AOF 记录日志

如下图所示:当客户端发起写的命令时,Redis 首先会执行写的命令,然后再将命令记录日志到磁盘中保存,除非磁盘损坏,日志记录是不会消失的。读的命令不需要进行记录日志,因为读取数据并没有对数据进行修改。
在这里插入图片描述
从上图可以直到,Redis 首先执行的是写命令,再执行的是记录日志到磁盘中,那么为什么不先记录日志到磁盘中,再执行写命令呢?其实会出现以下问题:

  1. 先执行记录日志的命令,如果这个写的命令有错误,Redis 直接记录到了磁盘,等后续从磁盘中恢复数据,就可能会出错,这样就需要再进行写磁盘之前进行校验。如果先执行写命令,这样在业务处理阶段就会进行命令的校验,然后可以直接写道磁盘中,减少校验的开销,从而保证记录到磁盘中的写命令是正确的;
  2. 先执行写磁盘的操作,会阻塞当前写命令的执行,如果写入磁盘的数据很多,那么执行写命令就会一直阻塞,也就是业务一直会阻塞下去。只有写操作命令执行成功后,才会将命令记录到 AOF 日志中,这样也不会阻塞业务逻辑的处理走向。

但是也不是没有风险的,上面这两步操作都是有潜在风险的:

  1. 如果写命令执行成功后,进行写磁盘操作的过程中,服务宕机了,那么这段时间执行的操作就会丢失;
  2. 在服务不宕机的情况下,这两步操作都是由主线程进行的,他们是同步进行的,写入磁盘的操作会阻塞接下来 Redis 写命令的进行。因为写入磁盘的操作就是一次用户态到内核态的一次 I/O 切换,如果在将日志内容写入到硬盘时,服务器的硬盘的 I/O 压力太大,就会导致写硬盘的速度很慢,进而阻塞住了,也就会导致后续的命令无法执行。

在这里插入图片描述
所以何时写入磁盘的时机就很重要了,Redis 提供了三种写入磁盘的频率,在 redis.conf 配置文件中的 appendfsync 配置项可以有以下 3 种参数可填,效率从低到高,分别是:always、everysec、no。

下面我画个表解释下这三种频率的含义

写入磁盘的时机效果
always字面意思,总是,就是执行一个写操作,就进行一次写入磁盘的操作
everysec每秒执行一次写入磁盘的操作
no不由 Redis 自己操作,交给操作系统自己去执行,也就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘

从上表可以看出,前面我讲过,Redis 写命令和写入磁盘是同步进行的,所以这时候就要进行取舍,针对业务对数据的敏感程度进行取舍:

写入磁盘的时机数据取舍
always最大程度保证数据不丢失,最坏情况会丢失最后一次执行的命令,性能开销大
everysec每秒执行一次写入磁盘的操作,最坏情况会丢失一秒的数据,性能开销相对always会低很多,但是性能开销感觉也还比较大
no性能开销低,完全交给操作系统去调度,但是宕机容易丢失很多数据

深入到源码后,你就会发现这三种策略只是在控制 fsync() 函数的调用时机:当应用程序向文件写入数据时,内核通常先将数据复制到内核缓冲区中,然后排入队列,然后由内核决定何时写入硬盘。Redis 是一种基于 单线程Reactor 模型的高效中间件。【图片来源于网络】

单线程Reactor模型

AOF 性能瓶颈

AOF 日志是一个文件,随着执行的写操作命令越来越多,文件的大小会越来越大。如果当 AOF 日志文件过大就会带来性能问题,比如重启 Redis 后,需要读 AOF 文件的内容以恢复数据,如果文件过大,整个恢复的过程就会很慢。Redis 为了避免 AOF 文件越写越大,提供了 AOF 重写机制,当 AOF 文件的大小超过所设定的阈值后,Redis 就会启用 AOF 重写机制,来压缩 AOF 文件。

AOF重写

AOF 重写机制是在重写时,读取当前数据库中的所有键值对,然后将每一个键值对用一条命令记录到新的 AOF 文件,等到全部记录完后,就将新的 AOF 文件替换掉现有的 AOF 文件。

重写工作完成后,就会将新的 AOF 文件覆盖现有的 AOF 文件,这就相当于压缩了 AOF 文件,使得 AOF 文件体积变小了,也就相当于新的值覆盖旧的值。在通过 AOF 日志恢复数据时,只用执行这条命令,就可以直接完成这个键值对的写入了。AOF 始终保持这最新的数据,用最新的命令去记录数据,恢复的时候,只需要执行最新的命令就好了。

为什么不直接复用现有的 AOF 文件,而是先写到新的 AOF 文件再覆盖过去?

因为如果重写的数据有问题,那么直接复用现有的AOF文件,如果重写 AOF 的过程中,这个数据出现了问题,那么 Redis 的 AOF 文件中就会出现类似数据库的脏数据,污染了整个文件,导致在恢复数据的时候出现问题。

AOF重写

在触发 AOF 重写时,就会对 AOF 文件进行重写,这时是需要读取所有缓存的键值对数据,并为每个键值对生成一条命令,然后将其写入到新的 AOF 文件,重写完后,就把现在的 AOF 文件替换掉。这个过程其实是很耗时的,所以重写的操作不能放在主进程里。Redis 的重写 AOF 过程是由后台子进程 异步来完成的,从而避免阻塞主进程。

子进程带有主进程的数据副本,这里使用子进程而不是线程,因为如果是使用线程,多线程之间会共享内存,那么在修改共享内存数据的时候,需要通过加锁来保证数据的安全,而这样就会降低性能。而使用子进程,创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读的方式,而当父子进程任意一方修改了该共享内存,就会发生写时复制,于是父子进程就有了独立的数据副本,就不用加锁来保证数据安全。

如下图所示:
主进程在通过 fork 系统调用生成子进程时,操作系统会把主进程的页表复制一份给子进程,这个页表记录着虚拟地址和物理地址映射关系,而不会复制物理内存,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。

在这里插入图片描述

在发生写操作的时候,操作系统才会去复制物理内存,这样是为了防止 fork 创建子进程时,由于物理内存数据的复制时间过长而导致父进程长时间阻塞的问题。

如果父进程的内存数据非常大,那自然页表也会很大,这时父进程在通过 fork 创建子进程的时候,阻塞的时间也越久。所以,有两个阶段会导致阻塞父进程:

  1. 创建子进程的途中,由于要复制父进程的页表等数据结构,阻塞的时间跟页表的大小有关,页表越大,阻塞的时间也越长;
  2. 创建完子进程后,如果子进程或者父进程修改了共享数据,就会发生写时复制,这期间会拷贝物理内存,如果内存越大,自然阻塞的时间也越长;
  3. 这个阶段修改的是一个 bigkey,也就是数据量比较大的 key-value 的时候,这时复制的物理内存数据的过程就会比较耗时,有阻塞主进程的风险。也就是第二点所讲,内存越大,阻塞时间越长,所以最好禁止在 Redis 中进行一些大 key 的操作。

重写 AOF 日志过程中,如果主进程修改了已经存在 key-value,此时这个 key-value 数据在子进程的内存数据就跟主进程的内存数据不一致了,这时要怎么办呢?

Redis 设置了一个 AOF 重写缓冲区,这个缓冲区在创建 bgrewriteaof 子进程之后开始使用。

在重写 AOF 期间,当 Redis 执行完一个写命令之后,它会同时将这个写命令写入到 AOF 缓冲区 和AOF 重写缓冲区。

也就是说,在子进程执行 AOF 重写期间,主进程需要执行以下三个工作:

  1. 执行客户端发来的命令;
  2. 将执行后的写命令追加到 AOF 缓冲区;
  3. 将执行后的写命令追加到 AOF 重写缓冲区

子进程执行操作后,会向主进程发送一条信号,信号是进程间通讯的一种方式,且是异步的。收到子线程的信号后,主线程会调用函数去执行重写 AOF 的命令,将新的 AOF 文件替换旧的 AOF 文件。这里是同步的,该函数执行完成后,主线程继续处理其它操作。

在整个 AOF 后台重写过程中,除了发生写时复制会对主进程造成阻塞,还有信号处理函数执行时也会对主进程造成阻塞,在其他时候,AOF 后台重写都不会阻塞主进程。

RDB 快照

RDB 快照就是记录某一个瞬间的内存中所有的数据,记录的全量数据,而 AOF 文件记录的是命令操作的日志,而不是实际的数据。所以从恢复数据角度来看,快照的效率是高于 AOF 的,因为 AOF 还需要执行写命令恢复数据,RDB 直接读入内存就好了。同时 RDB 丢失的数据取决于执行 RDB 的频率,一般快照几分钟执行一次,最差的情况会丢失这几分钟的数据。

RDB 有两种执行命令来生成 RDB 快照,一种阻塞式:save;一种非阻塞式:bgsave。bgsave会创建子进程去执行快照工作。

在bgsave快照命令下,RDB 也是可以和 AOF 一样,只有在发生修改内存数据的情况时,物理内存才会被复制一份,可以在快照的时候进行写时复制

在这里插入图片描述
RDB 快照式一次记录全量数据,如果主线程修改了共享数据,发生了写时复制后,RDB 快照保存的是原本的内存数据,而主线程刚修改的数据,是没有办法在这一时间像 AOF 那样写入 RDB 文件的,只能交由下一次的 bgsave 快照。因为此时主线程的内存数据和子线程的内存数据已经分离了,子线程写入到 RDB 文件的内存数据只能是原本的内存数据。最差的情况,如果系统恰好在 RDB 快照文件创建完毕后崩溃了,那么 Redis 将会丢失主线程在快照期间修改的数据。

最后

所以在 Redis 后续的进化中,将两种持久化结合到了一起,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。

这样做的好处就是在恢复 Redis 数据的时候,最开始式直接恢复 RDB 快照的数据,相对于直接使用 AOF 来说,效率会更高,并且主线程的写命令也会以 AOF 的形式记录下来,减少了更多数据的丢失。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JS debug跳过的几种方法

JS debug跳过的几种方法 第一种反调试解决方法: 禁用断点法 禁用所有断点,包括自己打的断点,无法调式。第二种反调试解决方法: 从来不执行法 在debug处 点击右键,选择 Nerver pause here,点击确定。第三种反调试解决方法: 条件…

常见的JS内置对象之数组

数组(Array) 1)定义数组 直接push的话,值放在了创建的大小之后,用下标进行操作值在创建大小里 创建两个空对象 2)数组的遍历: for in 循环 迭代数组:迭代出的是下标 拿到的不是值&am…

CnosDB x LangChain: 聊着天来查询时序数据库

本篇我们将主要介绍如何使用 LangChain 连接 CnosDB 数据库,实现使用自然语言和数据库的交流。 大模型等的相关话题已经霸榜半年有余,在讨论关注之余,CnosDB技术团队将大模型与人工智能相关技术与数据库开发与实践进行融合。继CnosDB全面整合…

实现最大子段和的计算,输入数据从文件读取。

1.设计思路   求解该题可以用分治法解题,总共有三种情况,最大子段在我们数组的左侧;最大子段在我们数组的右侧;最大子段跨过了左右两侧,在中间最大。第一种和第二种将两个子问题递归解出。分开的位置就是我们的中心位…

ios oc button 设置

Button调整内部的子控件的位置

图书借阅系统

系统描述 该图书借阅系统是一个用于图书馆或类似机构管理图书借阅流程的在线平台。系统主要包括管理员模块和读者模块,分别提供相应的功能和权限。 - 管理员模块: - 注册:允许管理员创建新的管理员账号。 - 登录:允许管理员使用账…

centos7 安装vmware17

vmware17 下载 官网 VMware Hong Kong - Delivering a Digital Foundation For Businesses | HK | HK 下载链接 Download VMware Workstation Pro 下载链接,直接点击下载即可 https://download3.vmware.com/software/WKST-1702-LX/VMware-Workstation-Full-17.0…

Unity自定义后处理——校色

Unity自定义校色后处理 大家好,我是阿赵。   之前介绍了一些后处理的做法,包括了PostProcessing和CommandBuffer。其实如果我们只是实现一些比较单一的全屏效果,也没必要搞那么复杂,直接在OnRenderImage里面写Graphics.Blit就可…

最近很火的 LangChain 你了解吗

最近 ChatGPT 很火,AIGC 很火,各类国产化 AI 很火。周边的 AI 工具集、框架也很火。各类新词也层出不穷。今天和大家学习和分享的是重量级新选手 LangChain。 一、什么是 LangChain LangChain 是一个 2023 年 1 月(v0.0.64)在 G…

网页聊天室测试

1.项目介绍: 该项目是一个基于网页的聊天室应用,具有用户注册、登录、会话列表、好友列表、发送消息等功能。下面是更详细的项目介绍: 用户注册和登录功能:用户可以通过输入用户名和设置密码来注册新账户,在注册过程中…

实施工程师需要了解的mysql知识

一、前言 做为实施工程师,在现场有时候是需要协助开发人员做一些简单的sql查询、更新动作的,不需要多复杂,下面就简单介绍下mysql的安装以及举一些最简单常用的sql用例。 二、安装mysql 2.1 下载安装包 第一步:在百度搜索"…

vue 通过多组复选框来过滤数据

1.通过if else 来筛选数据 <template> <div><div><label><input type"checkbox" v-model"checkedNames" value"北京"> 北京</label><label><input type"checkbox" v-model"chec…

使用python进行AI选股之同花顺问财

通过问财python库&#xff0c;可以输入中文就能获取AI选股结果&#xff0c;而不需要写很多代码来进行股票选取&#xff0c;如上图就是输入中文”均线多头排列的股票”获取的结果。本文主要介绍问财python库的选股功能和使用步骤。 什么是问财python库 同花顺i问财是同花顺旗下…

Echarts 定制化日历图

目录 改造一&#xff08;变化X轴、Y轴label&#xff09;&#xff1a; 改造二&#xff08;单元格、图例改造&#xff09;&#xff1a; 改造三&#xff08;tooltip、细节优化&#xff09;&#xff1a; 最近在做可视化&#xff0c;发现日历图挺有意思&#xff0c;分享一波吧&am…

SpringBoot项目打包Docker镜像

1、创建springboot项目&#xff0c;并在根目录新建Dockerfile文件 Dockerfile文件内容 FROM adoptopenjdk/openjdk8:jre8u-nightly ADD target/test-k8s-0.0.1-SNAPSHOT.jar test-k8s-0.0.1-SNAPSHOT.jar ENTRYPOINT ["java","-jar","/test-k8s-0.0.…

Python编程——字符串的拼接

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 本文专栏&#xff1a;python专栏 专栏介绍&#xff1a;本专栏为免费专栏&#xff0c;并且会持续更新python基础知识&#xff0c;欢迎各位订阅关注。 前言 本文将讲解字符串的拼接与使用%进行拼接。 目录 ​编…

(二)Qt下多线程实现多个海康工业相机内触发采集回调取流显示

系列文章目录 提示&#xff1a;这里是该系列文章的所有文章的目录 第一章&#xff1a;&#xff08;一&#xff09;Qt下实现多个海康工业相机内触发采集回调取流显示 第二章&#xff1a;&#xff08;二&#xff09;Qt下多线程实现多个海康工业相机内触发采集回调取流显示 文章目…

单例模式之常见模式详解

单例模式之常见模式详解 单例模式的定义单例模式的分类饿汉模式懒汉模式 单例模式的主要特点单例模式的应用场景总结 单例模式的定义 单例模式是一种设计模式&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供一个全局访问点来获取该实例。 在单例模式中&#xff0c;类…

2023需求最高的编程语言:Python、JavaScript和Java

科技招聘平台 CodinGame 和 CoderPad 最新发布了一份 2023 年科技行业招聘报告&#xff0c;基于对 14000 名专业人士的调查&#xff0c;对 2023 年科技行业招聘人员和求职者可能面临的情况进行了深入分析。其中一些关键亮点包括&#xff1a; 59% 的开发人员没有计算机科学方面的…

吴恩达机器学习笔记-week2

第2周 目录 第2周 四、多变量线性回归(Linear Regression with Multiple Variables) 4.1 多维特征4.2 多变量梯度下降4.3 梯度下降法实践1-特征缩放4.4 梯度下降法实践2-学习率4.5 特征和多项式回归4.6 正规方程4.7 正规方程及不可逆性(可选) 四、多变量线性回归(Linear Regres…