Redis数据持久化方案

news2024/12/26 12:25:50

作为集中式缓存的优秀代表,Redis可以帮助我们在项目中完成很多特定的功能。Redis准确的说是一个非关系型数据库,但是由于其超高的并发处理性能,及其对于缓存场景所提供的一系列能力构建,使其成为了分布式系统中的集中缓存的绝佳选择。

数据持久化方案

除了容量有限之外,数据丢失无疑是存储在内存中的数据最大的风险点。

因为内存中的数据是非持久化存储的,一旦断电或者出现系统异常等情况,很容易导致内存数据丢失。所以大部分的系统里面都只是将内存型缓存用作数据库的辅助扛压,最终的数据存储在DB等可以持久化存储容器中,同步一份数据到缓存中用于并发场景下的业务使用。

在这里插入图片描述

这种组网场景下,Redis的数据其实是没有持久化的诉求的,因为Redis中数据仅仅是一份副本,最终数据在DB中都有。即使系统异常或者掉电重启,也可以基于数据库的数据进行缓存重建 —— 最多就是数据量特别巨大的时候,重建缓存的耗时会比较长。

另外一种场景,业务里面会有有些写操作会比较频繁、强依赖Redis特性来实现的功能,这部分数据不能丢、但又没有重要到必须每次更新都需要存入DB的地步。比如博客系统中的文章阅读量数据,文章每次被读取都需要更新阅读数,写操作非常频繁,如果阅读量存储到DB中,会导致DB压力较大,这种情况就希望可以将数据存储在内存中,然后内存数据可以持久化保存。
在这里插入图片描述

Redis提供了多种持久化方案,可以实现将内存数据定期存储到磁盘上,重启时候可以从磁盘加载到内存中,以此来避免数据的丢失。

RDB全量持久化模式

全量模式很好理解,就是定时将当前内存里面所有的key-value键值对内容,全部导出一份快照数据存储到磁盘上。这样下次如果需要使用的时候,就可以从磁盘上加载快照文件,实现内存数据的恢复

RDB全量模式持久化将数据写入磁盘的动作可以分为SAVE与BGSAVE两种。所谓BGSAVE就是background-save,也就是后台异步save,区别点在于SAVE是由Redis的命令执行线程按照普通命令的方式去执行操作,而BGSAVE是通过Fork出一个新的进程,在新的独立进程里面去执行save操作。

  • Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程

  • 在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,Linux中引入了“写时复制技术

  • 一般情况父进程和子进程会共用同一段物理内存,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
    在这里插入图片描述

Redis的请求命令执行是通过单线程的方式执行的,所以要尽量避免耗时操作,而save动作需要将内存全部数据写入到磁盘上,对于redis而言,这一操作是非常耗时的,会阻塞住全部正常业务请求,所以save操作的触发只有两个场景:

  • 客户端手动发送save命令执行
  • Redis在shutdown的时候自动执行
    从数据保存完备性方面看,这两种方式都起不到自动持久化备份的能力,如果出现一些机器掉电等情况,是不会触发redis shutdown操作的,将面临数据丢失的风险。

相比而言,bgsave的杀伤力要小一些、适用度也更好一些,它可以保证在持久化期间Redis主进程可以继续处理业务请求。bgsave增加了过程中自动持久化操作的机制,触发条件更加的“智能”:

  • 客户端手动命令触发bgsave操作
  • Redis配置定时任务触发(支持间隔时间+变更数据量双重维度综合判断,达到任一条件则触发)

此外,在master-slave主从部署的场景中还支持仅由slave节点触发bgsave操作,来降低对master节点的影响。
值得注意的是,在fork子进程的时候需要将redis主进程中内存所有数据都复制一份到子进程中,所以bgsave操作实际上是将子进程内存中的数据快照导出到磁盘上,在执行期间对机器的剩余内存有较高要求,如果机器剩余内存不足,则可能导致fork的时候两份内存数据量超过机器物理内存大小,导致系统启用虚拟内存,拷贝速度大打折扣(虚拟内存本质上就是把磁盘当内存用,操作速度相比物理内存大大降低),会阻塞住Redis主进程的命令执行。

如果开启了RDB的bgsave定时触发执行机制,在出现异常掉电等情况,可能会丢失最后一部分尚未来及持久化的内容。在恢复的时候,Redis启动之后会先去读取RDB文件然后将其写入内存中恢复此前的缓存数据,数据恢复期间不受理外部业务请求。

优势

适合大规模的数据恢复、对数据完整性和一致性要求不高更适合使用、节省磁盘空间、恢复速度快

劣势

  1. Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑,

  2. 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能。

  3. 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改

AOF增量同步方式

RDB全量模式简单粗暴,直接将内存全量数据存储为快照序列化到本地。AOF(Append Only File)与RDB的思路不同,AOF更像是记录住Redis的每一次写请求执行命令,将每次执行的写操作命令记录存储到磁盘上,然后通过一种类似命令重放执行的方式,来实现数据的恢复。

AOF持久化流程

(1)客户端的请求写命令会被append追加到AOF缓冲区内;

(2)AOF缓冲区根据AOF持久化策略[always,everysec,no]将操作sync同步到磁盘的AOF文件中;

(3)AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容量;

(4)Redis服务重启时,会重新load加载AOF文件中的写操作达到数据恢复的目的

AOF具体实现的时候,包含几种不同的策略:
appendfsync always

可以简单的理解为每一条redis写请求执行的时候会触发一次磁盘写入操作,且只有在磁盘写入完成之后,请求的响应才会返回。这种方式可以保证AOF记录的准确性,但是会严重影响Redis的并发吞吐量。

appendfsync everysec

异步执行,任务执行线程执行命令后将命令写入任务放入队列中,由子线程异步方式每秒一次将执行命令分批写入文件中,相比always方式在异常情况下可能会丢失最后1s的执行记录,但可以大大降低对redis命令执行效率的影响。

appendfsync no

redis不控制落盘时间,由操作系统去决定什么时候该往磁盘flush,这种情况一般不推荐使用,无法准确掌控是否落盘,可靠性不够。

AOF的方式落盘持久化的时候,每次仅写入增量的部分,所以对系统整体运行期的影响较小,但随着系统在线运行时长的累加,AOF中存储的命令也越来越多,这样问题也随着出现:

  • AOF写入的方式类似与日志打印,将请求追加写入到磁盘文件中,文本文件未经过压缩,时间久了之后会占据大量磁盘空间,易造成磁盘满的问题。

  • 在需要从AOF文件回放重新构建缓存内容时,可能会耗时较久(相当于要将长期累积下来的写操作命令逐个重新执行一下)。

Rewrite压缩

AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制, 当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof

重写原理

AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),redis4.0版本后的重写,是指上就是把rdb 的快照,以二级制的形式附在新的aof头部,作为已有的历史数据,替换掉原来的流水账操作。

no-appendfsync-on-rewrite:

如果 no-appendfsync-on-rewrite=yes ,不写入aof文件只写入缓存,用户请求不会阻塞,但是在这段时间如果宕机会丢失这段时间的缓存数据。(降低数据安全性,提高性能)

如果 no-appendfsync-on-rewrite=no, 还是会把数据往磁盘里刷,但是遇到重写操作,可能会发生阻塞。(数据安全,但是性能降低)

触发机制,何时重写

Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发,重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定Redis要满足一定条件才会进行重写。

auto-aof-rewrite-percentage:设置重写的基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发)
auto-aof-rewrite-min-size:设置重写的基准值,最小文件64MB。达到这个值开始重写。

例如:文件达到70MB开始重写,降到50MB,下次什么时候开始重写?100MB
系统载入时或者上次重写完毕时,Redis会记录此时AOF大小,设为base_size,如果Redis的AOF当前大小>= base_size +base_size*100% (默认)且当前大小>=64mb(默认)的情况下,Redis会对AOF进行重写。

重写流程

(1)bgrewriteaof触发重写,判断是否当前有bgsave或bgrewriteaof在运行,如果有,则等待该命令结束后再继续执行。

(2)主进程fork出子进程执行重写操作,保证主进程不会阻塞。

(3)子进程遍历redis内存中数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区保证原AOF文件完整以及新AOF文件生成期间的新的数据修改动作不会丢失。

(4)子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息;主进程把aof_rewrite_buf中的数据写入到新的AOF文件。

(5)使用新的AOF文件覆盖旧的AOF文件,完成AOF重写。
在这里插入图片描述

优势

  • 备份机制更稳健,丢失数据概率更低。

  • 可读的日志文本,通过操作AOF稳健,可以处理误操作。

劣势

比起RDB占用更多的磁盘空间。恢复备份速度要慢。 每次读写都同步的话,有一定的性能压力。存在个别Bug,造成恢复不能。

RDB与AOF混合使用

RDB在过程中每次写磁盘的时候对Redis业务处理的性能影响较大,但是从磁盘加载到内存重建缓存的时候效率很高。

AOF通过增量的方式降低了运行过程中对Redis业务处理的影响,但是命令回放重建缓存的时候效率较差。
如果将两者结合起来使用,是否可以取长补短呢?事实似乎的确如此。从4.0版本开始,Redis支持了RDB + AOF的混合持久化方式,通过rewrite机制来实现。需要在redis的配置文件中开启对应开关:

aof-use-rdb-preamble yes

开启之后,redis在每次执行aof操作的时候会判断下是否达到了触发rewrite的条件,如果达到,则fork出一个新的子进程进行RDB操作将当前时刻全量内存数据生成RDB数据然后写入到AOF文件中,而后续的写操作命令则继续append方式追加记录到AOF文件中。这样一来AOF文件实际上由两部分内容组成。如下图所示:

通过RDB + AOF混合的策略,很好的实现了两者的优势互补:

  • 先通过AOF的方式记录命令,达到门槛的时候才执行rewrite操作生成RDB,最大限度降低了RDB执行频率,降低了对redis业务命令处理过程的影响。

  • 通过RDB的方式替代了前期大量的AOF命令存储,有效的降低了磁盘占用。

  • 通过RDB(恢复数据快) + AOF(解决RDB部分数据丢失问题)的方式,系统重建缓存的时候,先加载RDB文件完成主体数据的重建,然后在此基础上重放AOF增量命令,大大降低了启动时AOF重放的耗时。

两种策略同时开启

AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)

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

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

相关文章

深入学习Vue.js(十)异步组件和函数式组件

文章目录异步组件需要解决的问题异步组件实现原理1.封装defineAsyncComponent函数2.超时与error3.延迟和Loading组件函数式组件异步组件需要解决的问题 允许用户指定加载出错时要渲染的组件允许用户指定Loading组件,以及展示该组件的延迟时间允许用户设置加载组件的…

8. R语言绘图系统介绍、高级绘图与低级绘图、【绘图参数】、绘图函数包

b站课程视频链接: https://www.bilibili.com/video/BV19x411X7C6?p1 腾讯课堂(最新,但是要花钱,我花99😢😢元买了,感觉讲的没问题,就是知识点结构有点乱,有点废话)&…

筑基一层 —— 高质量C编程建议、详解猜数字游戏

目录 一.修炼必备 二.高质量C编程 2.1 高质量C编程的思维导图(需要思维导图的加qq:972606225获取) 2.2 文件结构 2.3 程序的形式 2.4 命名规则 三.猜数字游戏详解 一.修炼必备 1.入门必备:VS2019社区版,下载地址:Visual S…

torch_geometric -- Pooling Layers

torch_geometric – Pooling Layers global_add_pool 通过在节点维度上添加节点特征来返回批量图级输出,因此对于单个图 它的输出由下式计算 from torch_geometric.nn import global_mean_pool, global_max_pool, global_add_pool import torch as thf [[1,2,3,4…

Wider Face+YOLOV8人脸检测

YOLO系列的算法更新实在太快了,前些天刚学习完YOLOV7,YOLOV8就出来了。今天先理解模型的训练过程,后续再学习V8的网络结构等细节。YOLOV8源码链接:https://github.com/ultralytics/ultralytics1 数据格式转换Wider Face数据格式转…

java -- 14 多态、内部类、常用API

自动类型转换:多态下引用数据类型的类型转换强制类型转换案例:定义usb接口:定义鼠标和键盘的usb实现类,并有自己特有的方法,重写usb接口的方法,里面穿插了多态创建电脑类,把usb接口揉和进去&…

如何改变视频的MD5值?一分钟让你学会操作

肯定很多不是从事自媒体的朋友对MD5不是很熟悉,但其实它类似于人的身份证,只不过我们的身份证是一串数字,而它则是视频的后台编码,所以这也是一些平台用MD5来判断视频是否重复的依据。那么有人会问了,既然MD5这么特殊&…

Java实战:使用Hutool中的MailUtil实现邮件的发送

❤️作者主页:IT技术分享社区 ❤️作者简介:大家好,我是IT技术分享社区的博主,从事C#、Java开发九年,对数据库、C#、Java、前端、运维、电脑技巧等经验丰富。 ❤️荣誉: CSDN博客专家、数据库优质创作者🏆&…

EasyExcel的导入导出使用

1、说明 EasyExcel是阿里出的一款基于Java的、快速、简洁、解决大文件内存溢出的处理Excel的开源工具,本文主要是使用这个工具对Excel导入导出进行讲解。 官网: https://easyexcel.opensource.alibaba.com/github: https://github.com/alib…

达梦主备之备库失联后在线恢复加入集群

一、主库故障重启(备库接管前重启) 主库故障后立即重启,此时主库的守护进程变成 Startup 状态,重新进入守护进程的 启动流程,将数据一致的备库归档设置为有效状态,其余备库归档设置成无效状态,并…

一些工具软件的使用

文章目录010 Editor设置16进制编辑时每行显示的字节数使用列模式编辑VS Code基础操作定义快捷键配置导出/导入列模式将文件中的tab键转换为空格关闭插件自动更新博文链接Beyond Compare文件内容相同依然显示差异过滤文件或文件夹Excel使用组合,进行行、列的折叠使用…

【DX-BT24蓝牙模块-AT命令与手机透传教程】

【DX-BT24蓝牙模块-AT命令与手机透传教程】1. 前言1.1.串口基本参数1.2.AT命令模式和透传模式1.3.模块数据吞吐量2. 接线2.1 模块线序定义2.2 相关AT命令详解2.2.1 命令格式说明2.2.2 回应格式说明2.2.3 AT命令举例说明3. AT命令详解3.1 基础指令3.2 AT指令测试3.3 手机测试4. …

英华特在创业板提交注册:拟募资约5亿元,股权结构较为分散

近日,苏州英华特涡旋技术股份有限公司(下称“英华特”)在上海证券交易所递交注册。据贝多财经了解,英华特的上市申请于2021年6月30日获得受理,2022年8月25日获得创业板上市委会议通过。 本次冲刺上市,英华特…

智合同丨你还在为填写合同台账犯愁吗?

最近有朋友问我有没有什么智能化手段处理合同台账问题?对方表示合同台账管理实在是太麻烦了,工作量大,占用时间多。答案肯定是有的,那么首先我们来了解下合同台账。合同台账一般指合同台帐,包括合同登记台帐、合同检查…

Solidity 中的数学(第 1 部分:数字)

本文开启了一系列关于在 Solidity 中进行数学运算的文章。第一个要讨论的话题是:数字。 介绍 以太坊是一个可编程的区块链,其功能可以通过将称为智能合约的可执行代码片段发布到区块链本身来扩展。这将以太坊与第一代区块链区分开来,在第一代…

PointNet.pytorch点云三维深度学习论文与代码复现

PointNet.pytorch1. 概要1.1 文章1.2 点云和三维深度学习,PointNet 网络结构1.3 复现说明2. 代码2.1 代码逻辑2.2 思路详解3. 解析组会2023.1.18远程arcivdailyonedb1. 概要 1.1 文章 链接: 论文 1.2 点云和三维深度学习,PointNet 网络结构 链接: 三…

程序员必备素质:代码整洁之道

本次分享的内容是《代码整洁之道》,书中是以现实案例,以讲故事形式来总结归纳问题,并给出解决方案,很容易与我们产生共鸣。文中会有大量书中内容摘抄,都是个人认为很值得分享的内容。当然,也会有个人感悟&a…

【C++】从0到1入门C++编程学习笔记 - 基础入门篇:C++初识

文章目录一、第一个C程序1.1 创建项目1.2 创建文件1.3 编写代码1.4 运行程序二、注释三、变量四、常量五、关键字六、标识符命名规则一、第一个C程序 编写一个C程序总共分为4个步骤 创建项目创建文件编写代码运行程序 1.1 创建项目 Visual Studio是我们用来编写C程序的主要…

【C语言课程设计】通讯录(2.0版本)

前言 在前面的博客中,我们已经了解了通讯录的基本写法。当然那个通讯录是不够完善的。我们本小节对前面的通讯录做一次小升级,添加动态增容模块与利用枚举来优化选择语句 【C语言课程设计】通讯录(1.0版本)_青色_忘川的博客-CSDN博…

JVM-内存模型详解

JVM 把内存分为若干个不同的区域,这些区域有些是线程私有的,有些则是线程共享的,Java 内存区域也叫做运行时数据区,它的具体划分如下: 虚拟机栈 Java 虚拟机栈是线程私有的数据区,Java 虚拟机栈的生命周期…