Redis 持久化深度解析:深入理解 RDB 与 AOF 持久化机制

Hello , 大家好 , 这个专栏给大家带来的是 Redis 系列 ! 本篇文章给大家讲解的是 Redis 的持久化 . 在 Redis 中 , 分为 RDB 和 AOF 两种持久化方式 , 那我们就研究一下 RDB 和 AOF 之间具体的差别与关联 .

在这里插入图片描述

本专栏旨在为初学者提供一个全面的 Redis 学习路径,从基础概念到实际应用,帮助读者快速掌握 Redis 的使用和管理技巧。通过本专栏的学习,能够构建坚实的 Redis 知识基础,并能够在实际学习以及工作中灵活运用 Redis 解决问题 .
专栏地址 : Redis 入门实践

一 . 认识持久化

我们之前就接触过持久化这个概念 , 在 MySQL 的事务中 , 有四个重要的特性

  1. 原子性
  2. 一致性
  3. 持久性 : 跟持久化是一个概念
  4. 隔离性

在 MySQL 中是通过将数据存储到硬盘中来达到持久性 .

那所谓的持久性 , 我们可以这样理解

  • 把数据存放到硬盘上 , 是持久的
  • 把数据存放到内存中 , 是不持久的

通过重启进程 / 重启主机之后 , 如果数据依然存在 , 那么他就是持久的 .

而 Redis 是一个内存型数据库 , 他是把数据放到内存中的 , 但是内存中的数据并不是持久的 , 我们要想做到持久 , 就需要把 Redis 中的数据存放到硬盘上 .

那 Redis 就遇到了一个两难的问题 :

  • 为了保证数据快 , 我们还需要把数据存放到内存中 ;
  • 为了保证数据不丢失 , 数据还需要存放到硬盘中 .

那 Redis 采取的策略就是将数据都存放到内存和硬盘中 , 并且这两份数据理论上是完全相同的 .

那当我们插入一条新的数据 , 就需要将这份数据同时写入到内存和硬盘中 , 但是读取这份数据只需要直接从内存读取 . 而硬盘中的数据只有在 Redis 重启的时候 , 才需要将内存中的数据恢复到内存中 (就相当于一个备份)


在 Redis 中 , 实现持久化是按照这两种策略来实现持久化的

  1. RDB : Redis DataBase
  2. AOF : Append Only File

我们分别来看 RDB 和 AOF 这两种机制

二 . RDB

2.1 RDB 的触发时期

RDB 会定期的把我们 Redis 内存中的所有数据 , 都写入到硬盘中 , 然后生成一个快照

快照指的就是我们拍的照片 , 根据拍的照片还原出当时发生了一些什么 , 可以理解为还原犯罪现场的工具

那 Redis 就会给内存中存储的数据拍个照片 , 生成一个文件存储到硬盘中 , 也就是快照 .

后续 Redis 一旦重启了 , 也就是内存中存放的数据就被清空重置了 , 就可以根据存储的快照来去将数据恢复到内存中 .

那针对于 “定期” , 又有两种情况 :

  1. 手动触发 : 程序员通过 Redis 的客户端 , 执行特定命令 , 来触发快照的生成
    1. save : 执行 save 的时候 , Redis 就会全力以赴的进行 “快照生成” 工作 , 那就会阻塞 Redis 其他客户端的命令 , 导致 Redis 不能正常工作 , 进一步导致系统不能正常工作 ; 一般情况下我们并不建议使用 save 操作
    2. bgsave : backgroundsave , 指的是在背后进行保存 , 那也就代表 bgsave 并不会影响 Redis 主服务器处理其他客户端的请求和命令

那 Redis 是怎样做到 bgsave 的 , 是不是暗地里还是偷偷使用了多线程呢 ?

并不是 , 我们要知道 , 实现并发编程有很多方案 , 但是多线程只是其中一种 , 并不是只有多线程才能实现并发 . 而 Redis 使用的是多进程的方式来去实现的并发编程 , 从而完成的 bgsave 的实现

  1. 自动触发 : 在 Redis 的配置文件中设置让 Redis 每隔多长时间 / 每产生多少次修改 就触发持久化操作

2.2 RDB 的 bgsave 执行流程

那如果子进程是首次写文件 , 那还好 , 直接创建出一个空文件即可 ; 那如果子进程不是首次写入文件 , 那是不断创建出新的文件还是在之前的文件中追加内容呢 ?

我们通过模拟 , 可以观察 RDB 的镜像文件来去总结

2.3 RDB 的 rdb 镜像文件

Redis 生成的 rdb 文件 , 是存放在 Redis 的工作目录中的

我们可以在 Redis 的配置文件中进行设置 , 通过 vim /etc/redis/redis.conf 打开配置文件

这个选项就代表 rdb 文件的存储路径

在这个路径下面的 dump.rdb 就是我们的 RDB 机制生成的镜像文件了

这个文件是一个二进制文件 , Redis 会将内存中的数据压缩到二进制文件中

我们尝试打开 , 也会发现一些熟悉的面庞

接下来我们可以来具体看一下 RDB 执行的流程

2.4 RDB 效果演示

我们开启两个终端 , 一个打开 Redis , 一个找到 dump.rdb 文件

dump.rdb 存放在 /var/lib/redis

然后在最刚开始 , 清空一下数据库 , 然后去观察一下 dump.rdb 文件

然后插入一些数据之后 , 再去观察一下 dump.rdb 文件

我们发现并没有什么变化 , 这是因为我们在最刚开始就给大家讲过

RDB 的触发时机 :

  1. 手动触发 : 执行 save / bgsave 命令
  2. 自动触发 : 在配置文件中进行设置

那我们刚才只是普通的插入几个键值对 , 并不能满足触发条件 .

那这个自动触发中 , 什么情况下才能自动触发 RDB 呢 ? 我们也可以在 Redis 的配置文件中找到

虽然这些数据能够手动配置 , 但是要有一个基本原则 : 生成一次 RDB 快照 , 是一个成本比较高的操作 , 不能够频繁地触发 RDB 操作 , 而且我们也并不推荐大家随便修改配置文件 .

正因为 RDB 生成的不能过于频繁 , 这就导致快照中的数据和当时实际的数据情况可能存在一定偏差 , 比如 : 现在是 12:00 , 我们的机制是在 15min 之后才能触发 RDB 操作 , 但是恰恰在这个时间段 Redis 服务器宕机了 , 那恢复数据只能恢复 12:00 之前的数据 , 12:00 之后的数据就丢失了

那我们接下来模拟一下几种不同的情况

手动执行 save / bgsave 触发一次生成快照

在手动执行 bgsave 之后 , 我们隐隐约约就可以看到一些刚才存储的一些的信息了

那我们强制重启一下服务器 : service redis-server stop

然后看一下服务是否已经停掉了 : ps aux | grep redis-server

然后再重新启动 Redis 的服务器 : service redis-server start

再去重新启动 Redis 的客户端 : redis-cli

我们就可以获取到之前的数据了

插入新的 key , 但是不手动执行 bgsave

我们先来看一下目前已经存在的键值对

我们也去看一下 RDB 文件 : vim /var/lib/redis/dump.rdb

RDB 文件现在是这样

那我们新添加一个键值对 : k4

然后我们重新查看一下 dump.rdb

然后此时 , 我们不执行 bgsave 操作 , 我们直接重启 Redis 服务器

使用命令 : service redis-server restart

那按照正常情况 , 我们并没有达到生成快照的要求 , 所以重启 Redis 服务器 , k4 这个键值对就会消失

但是事情是这样吗 ?

Redis 生成快照的操作 , 不仅仅是手动执行命令才会触发 , 他也可以自动触发 .

我们刚才介绍了 , 通过配置文件中设置的多少时间内修改多少次来达到自动触发 , 这是一种方式 ;

那还有两种情况 , 也会自动生成 RDB 快照

  1. 通过 shutdown 命令关闭 Redis 服务器 , 也会触发 RDB 操作 (就是 service redis-server restart 命令)
  2. [后续介绍] Redis 在进行主从复制的时候 , 主节点也会自动生成 RDB 快照 , 然后将 RDB 快照文件的内容传输给从节点

那我们讨论的情况 , 是插入新的 key , 但是不手动执行 bgsave , 我们的想法是这个新的 key 就丢失了 , 但是刚才介绍的三种情况 , 并不会让新的 key 丢失 . 那我们刚才介绍的都是理想情况 , 如果 Redis 服务器突然宕机 / 突然掉电 … 情况 , 数据就不一定保得住了

我们可以模拟一下突发情况

先设置一个新的键值对

然后通过 kill 命令以极快的速度杀死 Redis 进程 , 这样 Redis 进程反应不过来就不会触发生成快照操作

此时继续连接到 Redis 服务器

通过配置自动生成 RDB 快照

我们只需要修改 Redis 的配置文件 , 就可以通过配置自动生成 RDB 快照

我们将默认的配置注释掉 , 然后自己添加一个配置 : save 60 2 , 代表 60s 内修改两次数据 , 就自动生成快照

然后保存配置文件

按住 Esc , 然后输入 :wq 即可

重启 Redis 服务器

使用命令 service redis-server restart

然后我们先清空一下数据库 , 然后构造超过两组的数据

等待 60s 之后 , 我们去看一下 dump.rdb 文件

要注意的是 , 有一个特殊操作 , 当我们配置 save “” 这样的空字符串的时候 , 就代表我们关闭自动生成快照 . 那 Redis 就纯纯的变成了一个真正的内存数据库了 .

如果故意改坏 RDB 文件

我们打开 dump.rdb 这个文件 : vim /var/lib/redis/dump.rdb

然后使用 kill -9 进程 ID 这个命令来去杀死进程

注意 : 不能使用 service redis-server restart 这个命令进行重启 , 这个命令会在 Redis 服务器退出的时候重新生成 RDB 快照 , 就会将我们故意修改的 RDB 文件覆盖掉

然后我们再去查看一下之前修改过的 dump.rdb 文件

那我们再连接 Redis 客户端 , 查看一下能否正常访问之前的键值对

那看起来没毛病不代表真的没毛病 , 只不过使我们修改的轻了 . 如果改坏的位置是末尾 , 那就对前面的内容没什么影响 ; 但是如果改坏的位置是中间 , 这就说不准了

按 Esc 键 , 然后输入 :wq 进行保存

此时我们再去杀死 Redis 服务器进程 , 使用 kill -9 进程 ID 命令

那我们手动启动 Redis 呢 , 使用 service redis-server start 来去启动 Redis

完喽 , 搞坏了~

我们可以通过 Redis 的日志来看一下错误原因 , Redis 的日志存放在 /var/log/redis 中 , 使用 cd /var/log/redis 来进入到该目录

红色字体的都是对日志的压缩包

使用 vim redis-server.log 来去查看日志

在最下面就打印出了错误日志

那我们也看出来了 , RDB 文件是二进制的 , 如果我们把坏了的 RDB 文件交给 Redis 服务器来去使用 , 那得到的结果是不可预期的 , 有可能服务器能够启动但是获取到的结果可能存在问题 , 但是也有可能 Redis 服务器直接启动失败 .

那 Redis 也料想到了 , 就提供了 RDB 文件的检查工具 , 我们可以先通过检查工具来去检查一下 RDB 文件的格式是否符合要求

首先进入到 /uer/bin 目录 , 使用 cd /uer/bin 命令

然后列举出 Redis 一系列的工具 , 使用 ll redis-* 来去查看

我们可以使用一下这个工具 , 使用命令 redis-check-rdb dump.rdb 就可以进行检查

2.5 bgsave 底层细节

当执行生成 RDB 镜像操作的时候 , bgsave 就会创建出一个子进程 , 通过子进程来完成持久化操作 .

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

子进程他操作的速度太快 , 我们不便观察 . 但是我们可以观察一下新的文件替换旧的文件这个操作

我们可以通过 Linux 的 stat 命令 , 查看文件的 inode 编号 . 如果编号发生变化 , 那就代表不是同一个文件

那在执行 bgsave 之前 , 先看一下 dump.rdb 文件的 inode 编号

然后执行 bgsave 操作

然后再查看一下 dump.rdb 文件的 inode 编号

编号发生了变化 , 这就代表文件不再是同一个文件了

如果使用的是 save 命令 , 那就不会触发生成子进程然后替换文件的逻辑 , 只是让主进程来去完成替换文件的逻辑

三 . AOF

AOF 的全称是 append only file , 意思就是在文件后面追加

他就类似于 MySQL 中的 binlog , 将用户的每个操作都记录到文件中 . 当 Redis 重新启动的时候 , 就会读取 AOF 文件中的内容 , 用来恢复数据 .

❓ 那 Redis 服务启动的时候 , 到底是读取 RDB 还是读取 AOF 呢 ?

✔️ Redis 会优先读取 AOF , 当我们开启 AOF 的时候 , RDB 就会失效 , 就不会在读取 dump.rdb 文件的内容了

3.1 AOF 的基本使用

AOF 默认是关闭状态 , 我们需要修改配置文件然后开启 AOF 功能

打开 Redis 的配置文件 : vim /etc/redis/redis.conf

然后在下面输入 /appendonly 就可以进行搜索

我们需要修改这个配置为 yes

AOF 文件所在的位置 , 跟 RDB 文件所在的位置一样 , 都是在 /var/lib/redis 目录中

设置结束之后我们重新开启一下服务器 : service redis-server restart

既然能够启动 , 就代表我们现在读取的不是 dump.rdb 了 , 而是 appendonly.aof 文件了

因为我们之前手动的把 dump.rdb文件修改坏了导致 Redis 服务器启动失败

那我们也可以看一下 /var/lib/redis 目录中是否出现了 appendonly.aof 文件 , 使用 cd /var/lib/redis 就可以跳转到对应目录

其实我们现在已经可以把 dump.rdb 文件删除掉了 , 因为接下来都是读取的 appendonly.aof 文件了

使用命令 : rm -rf dump.rdb 即可删除

AOF 持久化的策略是每一次操作都会写入到 appendonly.aof 文件中

可以看出 , AOF 是一个文本文件 , 每次执行的操作都会保存到 appendonly.aof 这个文本文件中 , 然后通过一些特殊的符号作为分隔符 , 对各个命令进行区分 .

那此时 , 如果我们重启 Redis 服务器 , 也是能够正常读取 appendonly.aof 中的内容的

通过 ps -aux | grep redis 来去查找 Redis 服务的进程 ID , 通过 kill -9 进程 ID 来去杀死对应的进程

此时我们再去获取之前设置的数据也是没问题的

3.2 AOF 是否会影响到 Redis 的性能 ?

我们刚才也了解到了 , AOF 是一个文本文件 . 那相比于 RDB , AOF 又要写内存 , 又要写硬盘 , 这不是拖慢了速度吗 ?

但是实际上 , 是并没有影响的 , 他并没有影响到 Redis 处理请求的速度

  1. AOF 机制并不是直接让工作线程把数据写入到磁盘中 , 而是先写入到内存中的一个缓冲区 , 然后一定程度之后再写入到硬盘 , 这样就大大的减少了写硬盘的次数
  2. 在硬盘上读写数据也不都是很慢的 , 一般来说顺序读写的速度也是比较快的 (但是仍然比内存慢) , 但是随机访问速度就比较慢了 . 那我们的 AOF 是顺序读写 , 也不算太慢 .

3.3 AOF 缓冲区的刷新策略

我们刚才介绍了 , AOF 存储数据的策略是先把一系列的数据存储到内存中的一个缓冲区中 , 等待一定程度之后再写入到磁盘中 .

那就有一种可能 , 如果我们把数据写入到缓冲区中 , 他本质还是存放到内存中啊 . 如果突然 Redis 进程挂了或者主机掉电了 , 怎么办啊 ? 缓冲区的数据还能保得住吗 ?

很遗憾 , 缓冲区的数据如果没写入到 appendonly.aof 中 , 那就找不到了 .

那 Redis 还做不到既快又能持久化这样的特点 , 但是 Redis 给出了一些选项 , 可以让程序员根据实际情况来去设置缓冲区的刷新策略 .

刷新频率越高 , 性能影响就越大 , 同时数据的可靠性就越高 ; 刷新频率越低 , 性能影响就越小 , 数据的可靠性就越低 .

Redis 提供了三个级别的刷新策略

选项刷新策略邓丽
always每执行一个写命令就保存一次刷新频率最高 , 数据可靠性最高 , 性能最低
everysec每秒保存一次刷新频率降低 , 数据可靠性降低 , 性能会提高
no操作系统决定刷新频率刷新频率最低 , 数据可靠性最低 , 性能最高

我们 Redis 默认的策略是每秒刷新一次缓冲区

3.4 AOF 的重写机制

随着命令的不断追加 , AOF 文件体积也会持续增长 , 而 Redis 在启动的时候会去读取 AOF 文件的内容 , 那就会影响到 Redis 下次启动的启动时间 .

并且 AOF 中的文件也有可能是冗余的 , 比如这样的一组命令

set key 111
set key 222
set key 333

实际上最后的结果就是 set key 333

再或者说

set key 111
del key
set key 222
del key

那这组命令就等价于什么都不做

我们也可以看到 , AOF 文件记录了中间的过程 , 但是实际上 Redis 在重新启动的时候 , 只需要关注最终的结果 .

因此 Redis 就提供了一个机制 , 能够对 AOF 文件进行整理 , 将冗余的操作剔除掉 , 达到给 AOF 文件瘦身的效果 , 这个机制就是 AOF 重写机制 .

比如我们的记账软件 , 我们不关心钱都花在哪了 , 或者有多少钱进账了 , 我们只关心余额剩多少 , 中间反反复复的运算我们并不关心 .

3.4.1 触发时机

AOF 的重写过程也分为两种 :

  • 手动触发 : 执行 bgrewriteaof 命令
  • 自动触发 : 根据配置文件中 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 来确定自动触发的时机
    • auto-aof-rewrite-min-size : 文件大小
    • auto-aof-rewrite-percentage : 当前 AOF 文件大小相比于上次重写 AOF 的文件大小所增加的比例

我们重点关注一下 AOF 重写的流程

3.4.2 重写流程

AOF 重写机制.png

核心就是 fork 之前的数据直接写入到新的 AOF 文件中 , fork 之后收到的数据先由父进程暂存到 aof_rewrite_buf 缓冲区中 , 当子进程完成新的 AOF 文件写入之后再把 aof_rewrite_buf 缓冲区中的数据保存到新的 AOF 文件中

❓ 那如果我们执行 bgrewriteaof 命令 , 但是此时 Redis 已经正在进行 AOF 重写 , 会怎么办呢 ?

✔️ 此时 , 并不会再执行 AOF 重写 , 直接就返回了

❓ 那如果我们执行 bgrewriteaof 命令 , 但是此时 Redis 已经正在进行 RDB 重写 , 会怎么办呢 ?

✔️ 此时 , AOF 重写操作就会等待 , 等待 RDB 快照生成完毕之后 , 再进行执行 AOF 重写

❓ RDB 对于 fork 之后的新数据 , 是不去处理的 ; 但是 AOF 对于 fork 之后的新数据 , 就采用了 aof_rewrite_buf 缓冲区的方式来去处理的 . 那 RDB 为什么不参考 AOF 的实现思路来去实现 , 这样不就不会丢失数据了

✔️ RDB 的设计理念就是 “定期备份” , AOF 的设计理念才是 “实时备份”

❓ 父进程调用 fork 之后 , 就会让子进程去编写新的 AOF 文件 . 当子进程写完新的 AOF 文件的时候 , 就会通知父进程将 aof_rewrite_buf 缓冲区的数据也保存到新的 AOF 文件中 , 但是此时父进程依然在继续写马上就会被替换掉的旧的 AOF 文件 , 这有意义吗 ?

✔️ 有意义 , 我们要考虑一些极端情况 , 假设在 AOF 重写的过程中 , 重写了一半服务器挂了 . 那子进程中内存的数据就会丢失 , 新的 AOF 文件还不完整 , 所以如果父进程不继续保存数据到 AOF 文件 , 那重启之后就没有一个完整的 AOF 文件了 .

四 . 混合持久化

我们先来构造一组重复的数据

127.0.0.1:6379> set key 111
OK
127.0.0.1:6379> set key 222
OK
127.0.0.1:6379> set key 333
OK
# 上面做的都是无用功 , 最后 key 保存的就是 444
127.0.0.1:6379> set key 444
OK

然后我们此时来看一下 AOF 文件 , 使用命令 vim /var/lib/redis/appendonly.aof

然后翻到最底下

然后记录一下当前 appendonly.aof 文件的 inode 编号

此时 , 我们使用 bgrewriteaof 这个命令进行 AOF 重写

然后我们此时再来看一下 appendonly.aof 文件的内容 , 是否进行了简化 , 使用命令 vim /var/lib/redis/appendonly.aof

此时 , AOF 文件内容长度大大减少 , 同时数据也按照了二进制的方式进行了保存

但是有点似曾相识啊 , 这怎么跟 dump.rdb 长得这么相像

实际上 , AOF 原本是按照文本的方式来写入文件的 , 但是按照文本的方式写入文件 , 后续加载的成本是比较高的 . 所以 Redis 引入了混合持久化的方式 , 他结合了 AOF 和 RDB 的特点 .

首先 , 按照 AOF 的方式 , 将每一个请求都记录到 AOF 文件中 , 然后在触发 AOF 重写只会 , 就会把当前的内存状态按照 RDB 的二进制格式写入到新的 AOF 文件中 .

那我们之后再去添加新的数据 , 依然是按照 AOF 文本的方式追加到文件后面 .

当 Redis 上同时存在 AOF 文件和 RDB 快照的时候 , 以 AOF 文件为主 , 因为 AOF 中包含的数据比 RDB 中更全一些 .

五 . 对于信号的理解

在 bgsave 和 AOF 的重写中 , 都出现了一个步骤 : 信号通知父进程

那什么叫做信号呢 ?

信号可以认为是 Linux 系统中的神经系统 , 可以传递一些简单的信息 .

像我们上述的信息传递 , 只需要子进程简单的表达一下 “我干完了” , 就可以使用信号来去通知父线程进行一些操作


对于 Redis 的持久化机制我们已经讲解完毕 , 如果对你有帮助的话 , 还请一键三连~
在这里插入图片描述

  • 17
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加勒比海涛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值