Redis——持久化

文章目录

Redis持久化

在这之前要提及到Mysql的事务的四个比较核心的特性

原子性 一致性 持久性 隔离性

这里mysql的持久性就是把数据存储在硬盘上,而redis数据是存在内存里的,要想做到持久,就需要让redis把数据存储到硬盘上

Redis相比于Mysql这样的关系型数据库,最明显的特点/优势就是 快,所以为了保证速度快,数据肯定还是得在内存中,但是为了持久,数据还得办法存储在硬盘上

Redis决定 内存中也存数据,硬盘上也存数据,这样的两份数据,理论上是完全相同的

  • 当要插入一个新的数据的时候,就需要把这个数据同时写入到内存和硬盘(说是两边都写,但是实际上具体怎么写硬盘还有不同的策略,可以保证整体的效率还是足够的高)
  • 当查询某个数据的时候,直接从内存读取
  • 硬盘的数据只是在redsi重启的时候,用来恢复内存中的数据
  • 代价就是消耗了更多的空间,同一份数据,存储了两遍(但是毕竟硬盘比较便宜,这样的开销并不会带来太多的成本)

Redis的两种持久化的策略

  1. RDB => Redis DataBase 定期备份:每隔一段时间,进行一次备份
  2. AOF => Append Only File 实时备份:只要有数据变动,立即备份

定期备份:RDB

RDB 持久化是把当前进程数据生成快照 保存到硬盘的过程,触发 RDB 持久化过程分为手动触发自动触发

触发机制
  • 手动触发:通过redis客户端执行特定的命令,来执行快照生成 (手动触发分别对应 save 和 bgsave 命令)

• save 命令:阻塞当前 Redis 服务器 ,直到 RDB 过程完成为止,对于内存比较大的实例造成长时间阻塞,基本不采用

• bgsave 命令:Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在 fork 阶段,⼀般时间很短。

Redis 内部的所有涉及 RDB 的操作都采用类似 bgsave 的方式。

  1. 执行 bgsave 命令,Redis 父进程判断当前进是否存在其他正在执行的子进程,如 RDB/AOF 子进
    程,如果存在 bgsave 命令直接返回。
  2. 父进程执行 fork 创建子进程,fork 过程中父进程会阻塞,通过 info stats 命令查看latest_fork_usec 选项,可以获取最近⼀次 fork 操作的耗时,单位为微秒。
  3. 父进程 fork 完成后,bgsave 命令返回 "Background saving started" 信息并不再阻塞父进程,可以继续响应其他命令。
  4. 子进程创建 RDB 文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。执行 lastsave 命令可以获取最后⼀次生成 RDB 的时间,对应 info 统计的 rdb_last_save_time 选项。
  5. 进程发送信号给父进程表⽰完成,父进程更新统计信息。
  • 自动触发:在Redis配置文件中,设置一下,让Redis每隔多长时间/每产生多少次修改 就触发

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

这里的dump.rdb文件,就是rdb机制生成的镜像文件,redis服务器默认就是开启了rdb的,这个镜像文件,是一个二进制的文件,把内存中的数据,以压缩的形式,保存到这个二进制文件中

后续redis服务器重新启动,就会尝试加载这个rdb文件,如果发现格式有错误,就可能会加载数据失败

rdb文件,虽然我们没有主动去动他,但是也有可能会出现一些意外问题,一旦通过一些操作(比如网络传输)引起这个文件被破坏,此时redis服务器就无法启动

  • redis提供了rdb文件的检查工具 redis-check-rdb

查看一下RDB文件里面是什么内容

里面都是二进制内容,当我们对redis文件中的数据进行增删查改时,这里的RDB文件并不会立即更新

rdb的触发时机:
  1. 手动(save,bgsave)
  2. 自动(配置文件中,进行设置)

save //seconds之内至少存在changes次key的修改

虽然此处的这些数值,都可以自由修改配置,但是此处修改上述数据的时候,要有一个基本规则

生成一次rdb快照,这个成本是一个比较高的成本,不能让这个操作执行的太频繁

正因为rdb不能生成的太频繁,这就导致,快照里的数据,和当前实时的数据情况可能会存在偏差

save 60 10000 (两次生成rdb之间的间隔,至少得是60s)

假设当前

12:00:00生成了rdb(硬盘上的快照数据和内存中一致)

12:00:01开始,redis收到了大量的key的变化请求

12:01:00生成下一个快照文件

在上述过程之间,redis服务器挂了,此时就会导致,12:00:00之后的这些数据,就丢了(数新的据只是在内存里,还没存到rdb)

手动执行save&bgsave保存测试

由于这里执行的数据比较少,所以瞬间就完成了,立即查看应该是有结果的,如果数据比较多,执行bgsave就需要消耗一定的时间,立即查看不一定就是生成完毕了

保存后查看rdb文件可以看到已经发生了修改

持久化验证,可以看到,通过上述操作,redis服务器在重新启动的时候,加载了rdb文件的内容,恢复了内存中之前的状态

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

  1. 通过配置文件中save执行M时间内,修改N次...
  2. 通过shotdown命令(redis里的一个命令)关闭redis服务器,也会触发
  3. redis进行主从复制的时候,主节点也会自动生成rdb快照,然后把rdb快照文件内容传输给从节点
不手动执行bgsave测试

插入新的key,不手动执行bgsave,重新启动redis服务器

如果是通过正常流程重新启动redis服务器,此时redis服务器会在退出的时候,自动触发生成rdb操作,但是如果是异常重启(比如kill -9或者服务器 掉电)此时redis服务器来不及生成rdb,内存中尚未保存在快照中的数据,就会随着重启而丢失了

bgsave操作流程测试

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

而子进程完成持久化的速度太快了(数据少),难以观察到子进程,但是我们可以通过文件的inode来验证bgsave操作流程

在redis客户端执行bgsave后

可以看到Inode从18685044变为了18685080

通过配置,自动生成rdb快照

这里执行flashall也会自动生成rdb快照

将时间设置为60秒 设置两次保存rdb

如果rdb文件故意改坏了,会怎样

如果改的是结尾的位置,基本没什么影响,如果改的hi中间的位置,那么redis服务器就会挂掉

  • redis提供了rdb文件的检查工具,可以先通过检查工具,检查一下rdb文件格式是否符合要求

运行的时候,加入rdb文件作为命令行参数,此时就是以检查工具的方式来运行,不会真的启动redis服务器

redis-check-rdb dump.rdb

RDB的优缺点
  • RDB 是⼀个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每 6 小时执行 bgsave 备份,并把 RDB 文件复制到远程机器或者文件系统中 (如 hdfs)用于灾备。

  • Redis 加载 RDB 恢复数据远远快于 AOF 的方式。

  • RDB 方式数据没办法做到实时持久化 / 秒级持久化。因为 bgsave 每次运行都要执行 fork 创建子进程,属于重量级操作,频繁执行成本过高。

  • RDB 文件使用特定二进制格式保存,Redis 版本演进过程中有多个 RDB 版本,兼容性可能有风险。

实时备份:AOF

Append Only File

类似于mysql的binlog,会把用户的每个操作,都记录到文件中,当redis重新启动的时候,就会读取这个aof文件中的内容,用来恢复数据

将这个appendonly的属性设置为 yes后,重启redis服务器,这里我执行两次set命令后查看 /var/lib/redis目录下的文件,发现多了一个文件appendonly.aof

++由文件内容可以看到,AOF是一个文本文件,每次进行的操作都会被录到文本文件中,通过一些特殊的符号作为分隔符,来对命令的细节进行区分++

AOF是否会影响到redis的性能

redis虽然是一个单线程的服务器,但是速度很快,引入AOF后,又要写内存,又要写硬盘,还能和之前一样快了吗?

实际上,是没有影响的

  1. AOF机制并非是直接让工作线程把数据直接写入硬盘,而是++先写入一个内存的缓冲区++,积累一波后,再统一写入硬盘(假设由100个请求,100个请求一次写入硬盘,比分100次写入一个请求要快得多,写硬盘的时候,写入硬盘的数据的多少,对于性能影响没有很大,但是写入硬盘的次数则影响很大)
  1. 硬盘上读写数据,顺序读写的速度是比较快的(还是比内存要慢得多)随机访问则速度是比较慢的,而AOF是每次把新的操作写到原有文件的末尾,属于 顺序写入

如果把数据写入缓冲区里,本质还是在内存中,万一这个时候,进程突然挂了,或者主机掉电了,咋办?是不是缓冲区中的数据就丢了?? 答案是的,缓冲区没有来得及写入硬盘的数据会丢的

redis给出了一些选项,让程序猿,根据实际情况决定怎么取舍,缓冲区的刷新策略

刷新频越越高,性能影响就越大,同时数据的可靠性就越高

刷新频率越低,性能影响就越小,数据的可靠性就越低~~

可配置值 说明 取舍情况
always 命令写入aof_buf 后调用 fsync 同步,完成后返回 频率最高,数据可靠性最高,性能最低
everysec 命令写入aof_buf 后只执行 write 操作,不进行 fsync。每秒由同步线程进行 fsync。 频率低一些,数据可靠性也会降低,性能会提高
no 命令写入 aof_buf 后只执行 write 操作,由 OS 控制 fsync 频率。 频率最低,数据可靠性也是最低的,性能是最高的

通过配置文件可以看到redis默认是everysec策略

AOF的重写机制

AOF文件持续增长,体积越来越大,会影响到redis下次启动的启动时间,redis启动的时候要读取aof文件的内容

redis存在一个机制,能够对aof文件进行整理操作,这个整理就是能够提出其中的冗余操作,并且合并一些操作,达到给aof文件瘦身这样的效果,例如lpush 111 ,lpush 222 合并为 lpush 111 222

重写触发方式
  • 手动触发:调用bgrewriteaof命令
  • 自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机。
    • auto-aof-rewrite-min-size:表示触发重写时AOF的最小文件大小,默认为64MB
    • auto-aof-rewrite-percentage:代表当前AOF占用大小相比较上次重写时增加的比例
AOF重写的流程

创建子进程fork,父进程仍然负责接收请求,子进程负责针对aof文件进行重写

注意: 重写的时候,不关心aof文件中原来都有啥,只是关心内存中的最终的数据状态!

子进程只需要把内存中当前的数据,获取出来,以AOF的格式写入到一个新的AOF文件中

此处子进程写数据的额过程,非常类似于RDB生成一个镜像快照,只不过RDB这里是按照二进制的方式来生成的,AOF重写则是按照AOF这里的要求的文本格式来生成的

在创建子进程的一瞬间,子进程就继承了当前父进程的内存状态,因此子进程里的内存数据是父进程fork之前的状态,fork之后,新来的请求,对内存造成的修改,是子进程不知道的

此时父进程这里又准备额一个aof_rewrite_buf缓冲区,专门放fork之后收到的数据,子进程这边,把aof数据写完之后,会通过信号通知一下父进程,父进程再把aof_rewrite_buf缓冲区中的内容也谢入到新AOF文件里

就可以用新的AOF文件代替旧的AOF文件了

如果,在执行bgrewriteaof的时候,当前redis已经正在进行aof重写了,会咋样呢?? ------------此时不会再次执行aof重写,直接就返回了

如果,在执行bgrewriteaof的时候,当前redis已经正在进行生成rdb文件快照,会咋样呢?? ------此时aof重写操作就会等待,等待rdb快照生成完毕之后,再进行执行aof重写

rdb对于fork之后的新数据,就置之不理了,aof则对于fork之后的新数据,采取了aof_rewrite_buf缓冲区的方式来处理

父进程fork完毕之后,就已经让子进程写心得aof文件了,并且随着时间的推移,子进程很快就写完了新的文件 ,要让新的aof文件代替旧的,父进程此时还在继续写这个即将消亡的旧的aof文件是否还有意义??

这里就要考虑到一种极端的情况

假设重写过程中,重写了一半,服务器挂了,子进程内存的数据就会丢失,新的aof文件内容还不完整,所以如果父进程不坚持写旧的aof文件,重启就没法保证数据的完整性

打开aof文件可以看到所有的操作都被记录了下来

查看一下执行bgrewriteaof之前的aof文件信息

执行一下bgrewriteaof再查看aof文件信息

查看aof文件内容会发现已经变了, 并且会变成二进制

为什么会变成二进制呢?

AOF本来是按照文本的方式来写入文件的,但是文本的写入方式,后续的加载成本是很高的,redis就引入了"混合持久化的方式",结合了rdb和aof的特点。按照aof的方式每一个请求/操作,都录入文件,在触发aof重写之后,就会把当前内存的状态按照rdb的二进制格式写入到新的aof文件中,后续再进行操作,仍然是按照aof文本的方式追加到文件后面的

在配置文件中,可以选择是否开启混合持久化

当redis上同时存在aof文件和rdb快照的时候,此时以谁为主?以aof为主!!rdb直接被忽略了(因为AOF中包含的数据比RDB更全)

总结

  1. Redis 提供了两种持久化方案:RDB 和 AOF。

  2. RDB 视为内存的快照,产生的内容更为紧凑,占用空间较小,恢复时速度更快。但产生 RDB 的开

    销较⼤,不适合进行实时持久化,⼀般用于冷备和主从复制。

  3. AOF 视为对修改命令保存,在恢复时需要重放命令。并且有重写机制来定期压缩 AOF 文件。

  4. RDB 和 AOF 都使用 fork 创建⼦进程,利用 Linux ⼦进程拥有⽗进程内存快照的特点进行持久化,

    尽可能不影响主进程继续处理后续命令。

相关推荐
程序员JerrySUN2 小时前
基于 RAUC 的 Jetson OTA 升级全攻略
java·数据库·redis
还是大剑师兰特4 小时前
Redis面试题及详细答案100道(01-15) --- 基础认知篇
redis·大剑师·redis面试
布朗克1684 小时前
MySQL UNION 操作符详细说明
数据库·mysql·union
IT小辉同学6 小时前
Spring Boot Redis 缓存完全指南
spring boot·redis·缓存
喵桑..7 小时前
视图是什么?有什么用?什么时候用?MySQL中的视图
数据库·mysql
奋进小子9 小时前
达梦DISQL执行SQL和SQL脚本
数据库·sql
EasyCVR9 小时前
视频汇聚系统EasyCVR调用设备录像保活时视频流不连贯问题解决方案
数据库·ubuntu·音视频·云存储·云端录像
YueiL10 小时前
Linux文件系统基石:透彻理解inode及其核心作用
linux·网络·数据库
cyhysr12 小时前
redis8.0.3部署于mac
redis·macos
老华带你飞12 小时前
数码论坛|基于SprinBoot+vue的数码论坛系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·毕设·数码论坛系统