当前位置: 首页 > news >正文

【redis-02】深入理解redis中RBD和AOF的持久化

redis系列整体栏目


内容链接地址
【一】redis基本数据类型和使用场景https://zhenghuisheng.blog.csdn.net/article/details/142406325
【二】redis的持久化机制和原理https://zhenghuisheng.blog.csdn.net/article/details/142441756

如需转载,请输入:https://blog.csdn.net/zhenghuishengq/article/details/142441756

redis的持久化机制和原理

  • 一,redis的持久化机制和原理
    • 1,RDB持久化
      • 1.1,save方式实现
      • 1.2,bgsave的实现
      • 1.3,rdb的优缺点
    • 2,AOF持久化
      • 2.1,aof原生命令
      • 2.2,aof重写
    • 3,rdb和aof优缺点
    • 4,rdb和aof混合持久化

一,redis的持久化机制和原理

在平常开发中,redis一般是做为缓存使用,但是在很多大型互联网公司中,都是很依赖redis,将数据存储在redis中,如果redis挂了,那么在重启之后内部的数据就会丢失,因此在redis层面,需要对内部的数据进行持久化。在redis持久化中,主要有两种方式:rdb和aof

1,RDB持久化

1.1,save方式实现

在redis中,默认使用的就是rdb这种持久化方式,可以通过redis中安装的redis.conf配置文件查看。redis配置的策略如下,可以通过配置save命令来实现持久化的的操作,在一定的时间段,配置一定的阈值,当触发到阈值时,将内存中的数据进行持久化。

save 900 1     # 在 900 秒内至少有 1 次写操作,进行快照
save 300 10    # 在 300 秒内至少有 10 次写操作,进行快照
save 60 10000  # 在 60 秒内至少有 10000 次写操作,进行快照

接下来通过详细案例测试一下,为了测试这个快照,我这边先修改一下配置,在一分钟内,只要有一次写入,那么就会触发这个快照机制

save 60 1  # 在 60 秒内至少有 1 次写操作,进行快照
CONFIG SET save "60 1" //直接在客户端里面操作,和上面这种方式2选1    

随后先清空原有的dump.rdb文件,此时redis中的rdb文件被清空,或者为了测试的话,可以直接先清空所有的key-value,通过 FLUSHALL 命令即可,但是如果是线上环境需谨慎操作

truncate -s 0 dump.rbd

在这里插入图片描述

随后执行几条插入操作的命令,插入完之后,最后在执行以下save命令。在执行命令如果报错error的话,那是因为没满足阈值触发的条件,最好和我一样,改成60s有一次写入就触发存储的机制

set zhenghuisheng:age 18
set zhenghuisheng:height 18
set zhenghuisheng:sex 18    

在这里插入图片描述

随后可以直接查看这个dumo.rdb文件,可以发现数据已经全部存进rdb文件中

在这里插入图片描述

通过数据可以得知,rdb文件是一种二进制文件,更加的适配与操作系统,如果宕机了想要数据恢复的话,那么使用这种二进制文件恢复肯定是效率最高的。

但是如果是使用这种save的方式进行持久化,也会带来部分性能问题,因为save这种方式是采用的 同步 的方式进行数据存储,安全性高,但是如果来了一个 大key,就是一个大对象,如果存一个大对象就要5s,那么此时整个redis都会被阻塞5s,跟内部的反应堆模式有关,那么就会严重影响redis的性能

1.2,bgsave的实现

既然上面的save会由于同步带来一些性能的问题,因此redis也给出了另一份解决思路,就是通过bgsave异步的方式来实现这个rdb快照,从而提高整体的性能的吞吐量。bgsave,即background,后台存储的意思

接下来依旧先演示一下bgsave的使用,先执行三条插入数据的命令,随后执行 bgsave 进行数据存储

set bgsave:001 test1
set bgsave:002 test2
set bgsave:003 test3

在这里插入图片描述

结果如下图,此时三条命令也已二进制的方式存入到dump.db的配置文件中

在这里插入图片描述

bgsave通过 写时复制(copy-on-write) 的方式实现异步操作,就是执行bgsave的主线程,在执行bgsave命令的那一刻,会fork出一个子线程来copy主线程中的所有数据,并且此时如果还有数据写入进来,会同时操作原主线程中的内存数据和新fork出现的子线程中的数据。

既然是使用异步的方式进行数据的持久化,那么就有可能会不安全,数据丢失等问题。

1.3,rdb的优缺点

优点

使用rdb的优点在于,内部采用的是二进制的方式进行数据存储,存储效率较高,如果宕机了要进行数据恢复的话,那么数据恢复也相对较快

缺点

缺点也很明显,首先就是选择存储的方式,即save和bgsave的方式,就是经典的高性能和高可用之间做选择。采用save方式可用保证高可用,数据相对安全,但是可能会出现整个系统出现阻塞问题;采用bgsave方式,通过写时复制的异步方式进行数据的存储,性能相对较快,但是可能造成数据丢失等不安全问题。

还有一个主要的问题,rdb是通过某种阈值触发的条件实现rdb存储,假设5分钟达到1000条命令存储一次,那么如果在这5分钟内的数据刚好到达了阈值临界点,但是此时redis宕机了,导致这5分钟的数据没有进行rbd的持久化,那么就可能会丢失5分钟的数据,在重启redis进行数据恢复的时候,这5分钟的数据将不能恢复

2,AOF持久化

虽然说在redis中默认的是使用rdb这种方式进行持久化,但是rdb这种方式很明显,可能会造成数据丢失比较多的情况,比如一个1w qps/s的系统,假设丢5分钟数据,那么就会丢失掉300万条数据,显然使用默认的方式是不太合理的

100000 * 60 * 5 = 300 0000

2.1,aof原生命令

因此redis引入了aof文件日志写入的这种操作,在redis.conf配置文件中,打开 appendonly 设置为yes即可

appendonly yes
CONFIG SET appendonly yes  //直接在客户端中操作,和上面这种方式的效果一样    

在开启完aof命令之后,接下来再往redis中插入几条数据

set aof:test1 aof1
set aof:test2 aof2
set aof:test3 aof3

在这里插入图片描述

数据插入完成之后,接下来查看这个appendonly.aof文件里面到底存的是什么,由于前期aof一直是开着,因此这里只看后面几条命令即可,如果直接cat把数据直接全部的加载到内存里面,把内存撑爆,导致线上的redis服务器宕机了,那就得不偿失了

//查看尾部几条命令
tail -f appendonly.aof

在这里插入图片描述

可以发现里面就是一些原生执行命令。然后加一些redis内部的一些识别符,在数据进行恢复的时候,可以供内部的c++程序所识别,从而实现数据的恢复。实现数据恢复也比较简单,就是把这些命令从上往下的全部的执行一遍,然后就能将数据恢复。

aof持久化也有对应的策略,其持久化的方式有以下三种,默认采用 appendfsync everysec 每秒同步一次数据

  • appendfsync always:每次写入操作都会立即将数据同步到磁盘。安全性最高,但性能最低。
  • appendfsync everysec:每秒同步一次数据,这是默认选项,性能和安全性之间的折中。
  • appendfsync no:由操作系统决定何时同步数据,这样性能最好,但在崩溃时可能丢失一些数据。

到这里为止,丢失问题的情况依旧存在,比如将1s内的数据再提交到对应的日志文件的时候,rdis宕机了,这就会导致可能丢失1秒钟的数据,相对于上面的rdb,已经属于是优化的操作。

但是aof也有属于自己的缺点,rdb采用的是二进制的压缩文件,文件大小相对较小,在做数据拷贝或者恢复的时候相对较快;而aof都是记录原生的命令,包括记录一些字符串长度等等,随着数据的增加,aof文件会越来越大,一次其数据拷贝或者做数据恢复会相对缓慢

2.2,aof重写

上面谈到了aof的缺点,会随着时间的推移,文件变得很大,甚至可能不小心一个cat命令,直接把服务器的内存打满,导致redis宕机这种可能,因此aof内部添加了aof重写功能。

举个例子,假设一个减库存的操作,假设商品id为1001,数量有10000件,那么预扣减内存的命令就如下

set 1001:stock 9999
set 1001:stock 9998
set 1001:stock 9997    

就是很明显,这个key不变,但是value在变,如果用aof存的话,可能要存10000条数据,那么aof重写的机制就是,只记录最终值。比如商品卖了5000件,常规的aof就存了5000条数据,但是通过aof重写优化之后,只存最终结果即可,这样在日志中只需要存一条,最后数据恢复时也只需要恢复这条最终的命令即可,其他的数据对于整个系统来讲都是垃圾数据。

set 1001:stock 5000

aof重写策略如下,如在文件达到32m时触发一次重写,在64m时又触发一次… 依次类推

auto-aof-rewrite-percentage:100% 触发AOF重写时,文件大小相对于上一次重写时的百分比阈值
auto-aof-rewrite-min-size:32m    触发AOF重写时,AOF文件的最小大小  //可以通过命令方式设置
CONFIG SET auto-aof-rewrite-percentage 100
CONFIG SET auto-aof-rewrite-min-size 32mb    

除了上面的设置之外,也可以手动的方式触发aof重写,就是通过执行这个 BGREWRITEAOF命令

BGREWRITEAOF

通过aof重写的方式,减少aof存储文件的大小,从而提高在数据做主从架构或者恢复时的效率

3,rdb和aof优缺点

通过上面的分析,接下来可以对rdb和aof二者之前做一个全面的对比。

rdbaof
实现方式save、bgsave原生方式、aof重写
存储方式二进制压缩存储文本命令存储
宕机恢复恢复速度快恢复速度慢
数据安全安全性较低安全性高,最多丢1s数据
启动优先级低,优先级低高,优先使用
主从同步不使用,同步数据少使用,可以同步更多数据

在实际开发中,也是可以优先的考虑使用aof的方式来设计持久化问题,并且不管是在数据做同步上,还是数据恢复上,aof的优势还是占优,都能获取到更多的数据,从而保证系统的高可用。

4,rdb和aof混合持久化

在redis4.0之后,引入了一个新的持久化方式,就是可以让rdb持久化和aof持久化结合使用。rdb的优点是采用二进制存储,存储内容效,aof的优点是存储的数据量多,数据丢失量少,因此结合二者优点,让rbd和aof混合持久化。

在使用这个混合持久化之前,分别需要开启aof持久化和混合持久化的命令。

# 启用AOF持久化
appendonly yes
# 启用混合持久化
aof-use-rdb-preamble yes

以aof的维度来讲,肯定是想减少文件的容量大小,并且恢复速度快点,以rdb的维度来讲,肯定是想多存点数据,因此根据这二者维度,可以在aof文件重写之后,将数据转成二进制的格式存到文件中,通过这种方式,通过aof来解决数据丢失问题,又通过rdb文件格式来减少空间存储,并且能再宕机之后更快的恢复数据。

还是通过上面的库存例子详细的说明一下到底什么是混合持久化,首先是开启aof持久化和混合持久化,其次是开启aof的重写功能

auto-aof-rewrite-percentage:100 触发AOF重写时,文件大小相对于上一次重写时的百分比阈值
auto-aof-rewrite-min-size:32m    触发AOF重写时,AOF文件的最小大小 

随后扣减10次库存,为了演示这个文件中的数据,先将 appendonly.aof 文件先清空

truncate -s 0 appendonly.aof 

在这里插入图片描述

随后执行10条扣减库存的操作,如下图,每次对库存进行减1的操作

在这里插入图片描述

执行完成之后,由于此时并没有触发到aof的重写机制,因此需要手动命令进行触发

BGREWRITEAOF

在这里插入图片描述

最后直接查看这个 appendonly.aof 文件,可以发现有部分的二进制文件,有部分的原生命令文件。

就是说那1s的数据会通过rdb的二进制文件进行持久化,在持久化的时候依旧有命令进来,那么暂时先用aof文件进行存储,在下一秒又将前一秒的数据转换成二进制文件存储,通过这种方式最多也只会丢一秒的数据。

相关文章:

  • 数据科学基石:解析属性类型体系——从标称到比率,全面洞察数据分类机制
  • 快速开发拍卖平台,成品源码如何满足你的需求?
  • python测试开发---前后端交互Axios
  • Apache Iceberg构建高性能数据湖
  • 软件开发人员需要了解的知识
  • 代码随想录算法训练营第十四天|递归 226.翻转二叉树 101. 对称二叉树 104.二叉树的最大深度 111.二叉树的最小深度
  • vue3【实战】响应式主题(实时获取页面比例,指定尺寸内按比例缩放,超过指定尺寸保持高度不变的图片)
  • 云原生|浅谈云原生中的对象存储之MinIO 的使用
  • 回归预测 | Matlab基于SO-SVR蛇群算法优化支持向量机的数据多输入单输出回归预测
  • 【Linux】常用指令【更详细,带实操】
  • 数据结构编程实践20讲(Python版)—01数组
  • idea2021git从dev分支合并到主分支master
  • 什么是机器学习?
  • 谷神后端$vs.proc.invoke.stock.loadMap
  • ngxin
  • @jsonView过滤属性
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • express如何解决request entity too large问题
  • git 常用命令
  • Github访问慢解决办法
  • Git的一些常用操作
  • Linux链接文件
  • Redux 中间件分析
  • SpiderData 2019年2月23日 DApp数据排行榜
  • Windows Containers 大冒险: 容器网络
  • Work@Alibaba 阿里巴巴的企业应用构建之路
  • 开源SQL-on-Hadoop系统一览
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 首页查询功能的一次实现过程
  • 通过npm或yarn自动生成vue组件
  • 通信类
  • 优化 Vue 项目编译文件大小
  • 原生 js 实现移动端 Touch 滑动反弹
  • 终端用户监控:真实用户监控还是模拟监控?
  • AI算硅基生命吗,为什么?
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • ​flutter 代码混淆
  • ​ubuntu下安装kvm虚拟机
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • #565. 查找之大编号
  • #Datawhale AI夏令营第4期#AIGC文生图方向复盘
  • #define、const、typedef的差别
  • #java学习笔记(面向对象)----(未完结)
  • #Ubuntu(修改root信息)
  • (12)目标检测_SSD基于pytorch搭建代码
  • (2022 CVPR) Unbiased Teacher v2
  • (AngularJS)Angular 控制器之间通信初探
  • (bean配置类的注解开发)学习Spring的第十三天
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (js)循环条件满足时终止循环
  • (备份) esp32 GPIO
  • (附源码)springboot电竞专题网站 毕业设计 641314
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致