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

【redis】什么是持久化之 RDB

什么是持久化

MySQL 的事务,有四个比较核心的特性:

  1. 原子性
  2. 一致性
  3. 持久性==>持久化(说的一回事)
  • 把数据存储在硬盘上==>持久
  • 把数据存在内存上==>不持久
  • 重启进程/重启主机之后,数据是否还存在
  1. 隔离性

Redis 是一个内存数据库,是把数据存在内存中的。内存中的数据并不是持久的,要想能做到持久,就需要让 Redis 把数据存储在硬盘上

Redis 相比于 MySQL 这样的关系型数据库,最明显的有点/优势==>效率高/快

  • 因为它的数据是存在内存上的

如何实现持久化

为了保证速度快,数据肯定还得再内存中,但是为了持久,数据还得想办法存储在硬盘上

  • redis 决定,内存和硬盘上都存数据
  • 这样的两份数据,理论上是完全相同的(实际上可能存在一个小的概率有差异,取决于我们具体怎么进行持久化)

  • 当要插入一个新的数据的时候,就需要把这个数据,同时写入内存和硬盘。
    • 实际上怎么写入硬盘,还有不同的策略,不会导致每次都要写两份,导致效率降低
  • 当查询某个数据的时候,直接从内存读取
  • 硬盘的数据只是在 redis 重启的时候,用来恢复内存中的数据的
    这样就既能保证高效,又能通过硬盘恢复数据,保证持久化的效果,代价就是消耗了更多空间(一份数据,存了两遍)(硬盘便宜,不会带来太多成本)

实现策略

  1. RDB --> Redis DataBase
  2. AOF --> Append Only FIle

RDB

定期的把我们 Redis 内存中的数据,都给写入硬盘中,生成一个“快照

  • Redis 给内存中当前存储的这些数据,赶紧拍个照片,生成一个文件,存储在硬盘中
  • 后续 Redis 一旦重启了(内存数据就没了),就可以根据刚才的“快照”,把内存中的数据给恢复回来

[!quote] 快照
某个案发现场,警察来了之后,会拉上警戒线,然后开始忙碌地拍照,记录现场==>后续就可以根据这些记录的照片,来还原出现场当前发生了什么

定期”具体又分为两种方式:

  1. 手动触发
    • 程序员通过 Redis 客户端,执行特定的命令,来触发快照生成(savebgsave
  2. 自动触发
    • Redis 配置文件中,设置一下,让 Redis 每隔多长时间/每产生多少次修改就出发

手动触发

执行 save 的时候,Redis 就会全力以赴的进行“快照生成”操作,此时就会阻塞 Redis 的其他客户端的命令

  • 类似于 keys * 的后果
  • 一般不建议使用

save 是在前台,bgsavebackground)是在后面偷摸进行,不会影响 Redis 服务器处理其他客户端的请求和命令

  • 这样既可以保证持久化
  • 又可以保证 Redis 可以正常去响应命令

bgsave

Redis 是怎么做到 bgsave 的呢?是不是偷偷搞了个多线程?

  • 并非如此,多线程是实现并发编程的场景的一种方式,但不是唯一
  • Redis 使用的是“多进程”的方式,来完成的并发编程,实现 bgsave

image.png

Redis 服务器(父进程)收到 bgsave 的命令之后,首先会进行一个判断

1 . 判定当前是否已经存在其他正在工作的子进程

比如现在已经有一个子进程正在执行 bgsave,此时就直接把当前的 bgsave 返回


2 . 如果没有其他的工作子进程,就通过 fork 这样的系统调用,创建一个子进程来

forkLinux 系统提供的一个创建子进程的 API

  • 如果是其他系统,比如 Windows,创建子进程就不是 forkCreatProcess
  • fork 创建子进程,简单粗暴,就是直接把当前的进程(父进程)复制一份,作为子进程。一旦复制完成了,父子进程就是两个独立的进程,就格子执行各自的了
    • 会复制 PCB、虚拟地址空间(内存中的数据)、文件描述附表…
    • 本来 redis server 中,有若干变量,保存了一些键值对数据。随着这样的 fork 的进行,子进程的这个内存里也会存在和刚才父进程中一模一样的变量

因此,复制出来的“克隆体“(子进程),内存中的数据就是和“本体”(父进程)是一样的。接下来安排子进程去进行“持久化”操作,也就相当于把父进程本体这里的数据给持久化了

父进程打开了一个文件,fork 了之后,子进程也是可以同时使用这个文件的

  • 导致了子进程持久化写入的那个文件,和父进程本来要写的文件是同一个

如果当前 redis 服务器中存储的数据特别多,内存消耗特别大,此时进行上述的复制操作,是否会有很大的性能开销?

  • 此处的性能开销其实挺小
  • fork 在进行内存拷贝的时候,不是简单无脑的直接把所有的数据都拷贝一遍,而是“写实拷贝”的机制来完成的
    • 如果子进程里的这个内存数据,和父进程的内存数据完全一样,此时就不会触发真正的拷贝动作(而是爷俩其实用一份内存数据)
    • 但是,其实这俩进程的内存空间,应该是各自独立的。一旦某一方针对这个数据进行了修改,就会立即触发真正的物理内存上的数据拷贝

在进行 bgsave 这个场景中,绝大部分的内存数据,是不需要进行改变的(整体来说这个过程执行的还挺快,这个短时间内,父进程中不会有大批的内存数据变化)

因此,子进程的“写实拷贝”不会触发很多次,也就保证了整体的“拷贝时间”是可控的,高效的


  1. 子进程负责进行写文件,生成快照的过程,父进程继续接收客户端的请求,继续正常提供服务

  1. 子进程完成整体的持久化过程之后,就会通知父进程干完了,父进程就会更新一些统计信息,子进程就可以结束销毁了

总结:
创建子进程,子进程完成持久化操作,持久化会把数据写入到新的文件中,然后使用新的文件替换旧的文件

  • 子进程不好观察,我们观察新旧文件(通过 stat 命令,查看文件的 inode 号)

RDB 文件

redis 生成的 RDB 文件,是放在 redis 的工作目录中的,也是在 redis 配置文件中进行设置的

image.png

  • RDB 机制生成的镜像文件,redis 服务器默认就是开启了 RDB
  • 这是一个二进制文件,把内存中的数据,以压缩(消耗一定 CPU 资源,但是能省空间)的形式,保存到这个二进制文件中
    • 最多打开看看就行了,不要乱改,一旦要是把数据的格式改坏了,就麻烦了
    • 后续 redis 服务器重新启动,就会尝试加载这个 RDB 文件,如果发现格式错误,就可能会加载数据失败

redis 提供了 RDB 文件的检查工具image.png|256

当执行生成 RDB 镜像操作的时候,此时就会把要生产的快照数据,先保存到一个临时文件中。当这个快照生成完毕之后,再删除之前的 RDB 文件,把新生成的临时的 RDB 文件名字改成刚才的 dump.rdb

  • 自始至终,RDB 文件始终只有一个

效果

RDB 文件中的数据,不是你这边插入了数据,就会立即更新的

  • image.png|438
  • 插入数据之后,观察 RDB 文件内容 image.png image.png|427
  • 发现没有变化,此时我们执行保存数据的命令image.png
    image.png|331

可以发现 RDB 文件内容更新了


RDB 的触发时机:

  1. 手动(save、bgsave)
  2. 自动(配置文件中,进行设置)

image.png

  • 900s 之后,并且至少存在一次 key 的修改,就会触发自动更新

这些数值都是可以自由修改的,但是此处修改数据的时候,有一个基本的原则:

  • 生成一次 RDB 快照,这个成本较高,不能让这个操作执行太频繁

redis 持久化生成快照操作,不仅仅是手动执行命令才出发,也可以自动触发

  1. 通过刚才配置文件中 save 执行 M 时间内,修改 N
  2. 通过 shutdown 命令(redis 里的一个命令)关闭 redis 服务器,也会触发(service redis-server restart)(正常关闭
  3. redis 进行主从复制的时候,主节点也会自动生成 RDB 快照,然后把 RDB 快照文件内容传输给从节点
  • 如果是通过正常流程重新启动 redis 服务器,此时 redis 服务器会在退出的时候,自动触发生成 RDB 操作
  • 但如果是异常重启(kill -9 或者服务器掉电),此时 redis 服务器来不及生成 RDB,内存中尚未保存到快照中的数据,就会随着重启而丢失
  • 若要修改 RDB 配置,修改完成后需要重启服务器
  • 如果 RDB 文件被改坏了,redis 服务器启动不了了,我们可以去看一下日志,查看报错信息

redis 提供了 RDB 文件的检查工具,可以先通过检查工具,检查一下 RDB 文件格式是否符合要求 image.png|301

  • 检查工具和 reids 服务器,在 5.0 版本是同一个可执行程序,但是我们可以在运行的时候加上不同选项
  • 在运行的时候,加上 RDB 文件作为命令行参数,此时就是以检查工具的方式来运行

image.png

  • 这里就显示 RDB 文件里面有错误

RDB 小结

  • RDB 是一个紧凑压缩的二进制文件,代表 redis 在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每 6 小时执行 bgsave 备份,并把 RDB 文件复制到远程机器或者文件系统中(如 hdfs)用于灾备
  • redis 加载 RDB 恢复数据远远快于 AOF 的方式
    • RDB 是使用二进制的方式来组织数据,直接把数据读取到内存中,按照字节的格式取出来,放到结构体/对象中即可
    • AOF 是使用文本的方式来组织数据
  • RDB 方式数据没办法做到实时持久化,因为 bgsave 每次运行都要执行 fork 创建子进程,属于重量级操作,频繁执行成本过高
  • RDB 文件使用特定二进制格式保存,redis 版本演进过程中有多个 RDB 版本,兼容性可能有风险
    • 老版本的 redisRDB 文件,放到新版本的 redis 中不一定能识别

如果确实需要有一些“升级版本”的需求,就可以通过写一个程序的方式,直接遍历旧的 redis 中的所有 key,把数据取出来,插入到新的 redis 服务器中即可


http://www.kler.cn/a/591958.html

相关文章:

  • TypeScript中的类型断言(type assertion),如何使用类型断言进行类型转换?
  • R语言零基础系列教程-03-RStudio界面介绍与关键设置
  • DeepSeek关键技术再总结
  • DNS解析查询工具
  • 设计模式(行为型)-观察者模式
  • Android第四次面试(Java基础篇)
  • 机器学习——深入浅出理解朴素贝叶斯算法
  • Redis项目:短信验证码登录
  • 【JDK17】开源应用服务器大比对
  • logparser日志分析详解
  • ubuntu20.04安装mysql-workbench
  • DataWhale 大语言模型 - 模型详细配置
  • conda 的 envs_dirs 配置出错
  • 解决 ECharts 切换图表时的 Resize 问题
  • 博客图床 VsCode + PicGo + 阿里云OSS
  • SQLark中如何进行数据筛选与排序
  • 批量测试IP和域名联通性2
  • Seaborn 数据可视化指南:核心功能与实战技巧
  • Android wifi的开关Settings值异常分析
  • Type-C:智能家居的电力革命与空间美学重构