【Redis】持久化

🐼为什么要有持久化

redis是一个内存型 的数据库,既然是内存型的,那么如果发生了异常,比如断电了,redis-server突然挂了,这都会导致内存中的数据还没来得及保存,数据丢失了...如果这些数据还挺重要的,那么内存中的这些大批量数据一次性全部丢了,代价可太大了...那能不能有个办法,能让内存中的数据有个备份,所以才有了持久化。

[🐼](#🐼 什么是持久化) 什么是持久化

跟MySQL中事务的四大特性其中之一要保证持久性一样:即把数据存储在硬盘上,那么就是持久的,存储在内存上,就是不持久的!

🐼如何做?

redis相比于MySQL的特点就是数据在内存中的,就是快!

而现在又要保证其数据的持久性,把数据扔到硬盘上。那么redis到底要保存在硬盘还是内存?答案是:全部都要!redis保证了在内存中存在数据,在硬盘上也要存在数据,硬盘上的数据"理论"(实际上可能不同,不过绝大部分数据都是一样的)上和内存中的数据一致,是内存数据的备份 ,当插入一个数据时,要同时把数据写到内存和硬盘上

读取 某个数据的时候,直接从内存中读取

硬盘的数据只是在redis重启时,用来恢复内存中的数据的!

但是既要把数据写到内存,又要把数据写到硬盘,这么效率是不是会慢很多呢?答案是,redis有自已的处理办法,能保证在不丢失效率的前提下,还保证了数据的持久性!

主要支持RDB (redis database)和 AOF (append only file)两种持久化机制,来实现上述功能的。

RDB和AOF有什么区别呢?先来浅浅理解一下:

RDB就是定期备份,比如每间隔固定个时间,就将redis上的所有数据备份一份到硬盘上

AOF就是实时备份,每向redis中插入一条数据,就立马向备份文件备份这条数据。

🐼RDB

RDB 持久化是把当前进程内存中的所有数据 定期 生成快照保存到硬盘的过程。快照是什么?和MySQL的事务中那个快照很相似,简单可以理解为警察办案来案发现场取证,这些拍的照片就是快照。

所以这里所谓的快照,就是当内存数据丢失,根据快照,再将数据恢复回来!

定期这里包括两种:手动触发和自动触发。

🍓手动触发

即我们手动执行命令来触发RDB持久化这个过程。手动触发主要有两个命令:save和bgsava。

其中save命令是redis主线程 去做的,他会**"全力以赴"**的去执行生成快照,那么就会阻塞其他客户端的请求,效果类似keys * ,所以不推荐使用save

而bgsave,顾名思义,background,是在后台去做的,就跟创建了个后台进程"类似",不会影响主线程处理其他客户端,所以,手动保存更推荐bgsave

bgsave如何做的,能够达到这种并发编程的效果呢?由于redis就一个单线程。所以Redis 进程执行fork操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束,发信号通知父进程。阻塞只发⽣在 fork 阶段,⼀般时间很短。如图:

以下所说的关于rdb的内容,手动触发和自动触发最后实现的效果都一样,都是同样的文件,同样的路径。只不过实现方式不同罢了。

我们上述所谓的硬盘层面的持久化,是保存在哪里呢?

其实是保存在一个后缀名为rdb的镜像文件 的,默认dump.rdb,手动触发和自动触发都是保存在这个文件中。我们可以从redis的配置文件中修改文件名,redis的配置文件路是/etc/redis/redis.conf。

redis.conf中默认rdb文件名:

dump文件所在路径:

✅什么是rdb文件:

rdb文件,是一个二进制文件。将内存中的数据,通过压缩的方式,保存在二进程文件中,这虽然消耗了一定的CPU资源,但是节省了空间。

🔎这就有一个问题了,如果我们不小心将rdb文件破坏了,那么是不是redis服务器就不能恢复数据了?是的!可能导致redis服务器无法启动。因为这也是RDB机制的弊端。如何做?下面验证会说!

最后在说一下,在形成快照,即dump.rdb文件时,不是直接在旧的dump.rdb文件上修改的,而是创建了一个新的rdb文件,这样做保证了如果突然发生了异常,dump.rdb还能保证其正确性。我们后面可以从inode上看到,当新的临时rdb文件拷贝完成,将新的rdb文件命名为dump.rdb。

🔎这里还有个问题:我们之前一直说redis是基于c-s的网络服务,可是为什么dump.rdb在我的本地,不是应该在服务器嘛?哈哈哈,因为你使用的 Redis 服务进程就运行在这台本地虚拟机上,之后有需要,当然可以把redis服务器部署到网络层面的主机

💮验证手动触发

1.通过bgsave来手动触发一次快照,即使重启后,依旧能读到重启前的数据。

我们后面知道了自动触发的条件就知道这里手动触发的意义,因为没有达到自动触发的条件,所以这里手动触发才有意义。假设我们现在插入3组数据:

由于数据较小,所以这个过程是很快的。但是如果数据多,那可就不一定了。

通过主动手动备份的方式,我们成功在重启redis服务器后,获得了我们之前插入的数据,证明了redis支持数据的持久化

2.验证bgsave创建子进程,并且用新文件替换旧文件!

由于创建子进程太快了,这里观察不到,这里仅看bgsave后,是否用新文件替换了旧文件

可以使用stat命令来观察文件的属性,包括inode编号,只要bgsave备份前后dump.rdb的inode不一样,足以验证了。


🍓自动触发

每隔一定条件,这里的条件比如每隔多长时间/多少次写入就自动触发一次。

每隔多长时间多久呢?我们可以从配置文件中修改,如图:

这个触发机制表示每隔seconds并且 改变了changes,redis会自动触发一次rdb快照生成

不过虽然这些配置是可以供我们手工修改的,但是我们不能修改的太频繁,因为生成一次快照是有代价的!因此,建议两个rdb文件生成的间隔至少为60s

✅可是,这样不就会造成一个问题,因为中间是有时间间隔的,那么假设现在12:00是第一次rdb快照,假设下一次快照是12.01,但是在12.00~12.01中间插入了很多数据key,还没到12.01,redis挂了,还没来得及触发12.01的快照,那么是不是这些数据依然没有持久化?是的!因为毕竟有时间间隔,来不及处理嘛。那么如何做?AOF会给出答案,但也不是完美的。

自动触发这个触发机制才是在实战中有价值的,下面我们来模拟几种情况

💮验证自动触发

  1. 通过修改配置文件,来达到自动触发的效果。我们这里就将条件设置为每隔60s,并且只要修改一次,就自动触发一次快照。

save "" 表示关闭自动生成快照

2.插入新的key,不手动执行bgsave,直接重启服务器?

我们发现,没有手动bgsave,但是我们插入的数据竟然还在!

其实redis快照,不仅仅支持手动触发, 还支持自动触发!支持3种方式:

  1. 通过刚刚的配置文件,使⽤ save 配置。如 "save m n" 表示 m 秒内数据集发⽣了 n 次修改,⾃动 RDB 持久化
  2. 从节点进⾏全量复制操作时,主节点自动进⾏ RDB 持久化,随后将 RDB ⽂件内容发送给从结点。这个后面再分享~
  3. 执行shutdown 命令关闭 Redis 时,自动执行 RDB 持久化 。也就是我们这种情况了~

3.插入新的key,不手动执行bgsave,而是直接将redis-server干掉(模拟了异常,比如服务器挂了,断点了...)。看看重启后,还能读出来异常之前的数据吗?

这种情况才往往是真实情况,比如断电,redis-server挂了...

如果真的是这样,那么redis-server还没来得及保存快照,就挂了,那么数据就真的丢了...

重启了数据仍然没有,所以更害怕这种情况,实际开发中。

4.如果把.rdb文件破坏了会咋样?

如图:

把.rdb文件破坏了,并且一定以kill- 9 杀掉服务器,不然在服务器退出的时候,别忘了也会自动生成快照,就把咱得坏的.rdb文件给替换成好的了。

我们发现,如果rdb文件坏了,可能启动不了,也可能能启动,但数据不保证了...如图所示,redis服务器启动不了了。

我们可以打开redis日志文件看看,一般linux系统的日志文件都在/var/lib//下,可以使用/var/lib/redis/redis-server.log来查看日志信息,为啥启动不了。

总之,当rdb文件坏了,那么就是不可预期 的,如果我们将错误的数据加载到内存,那还不如不启动成功呢,因此,edis提供了一个工具,其实就是一个命令,redis-check-rdb,我们可以从/usr/bin/中查到这个命令,通过跟上选项,就能知道这个dump.rdb是否出错!

因此,我们要有定期备份rdb文件的习惯,因为他本质就是一个文件嘛。避免了真的启动不了...

redis-check-rdb dump.rdb 就能检查该rdb文件是否遭到破坏。检查报错

可是这样我们的服务器还如何启动呢?可以参考这篇文章:

rdb损坏redis服务器如何启动

如果启动不了,可能还是其他原因,请检查,比如:

Redis 进程默认以 redis 用户运行,而 /var/lib/redis/ 下的 appendonly.aofdump.rdb 属于 root,权限不足导致启动失败。

最后,我们来总结一下,RDB持久化:

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

Redis 加载 RDB 恢复数据远远快于 AOF 的方式,RDB是二进制形式组织起来的文件,读取快,而AOF是文本的方式来组织数据的,有一系列的子串切分操作。

RDB 方式式数据没办法做到实时持久化 / 秒级持久化。因为 bgsave 每次运⾏都要执⾏ fork 创建⼦进程,属于重量级操作 ,频繁执⾏成本过高,因此,不能频繁执行,这就导致了RDB最大的问题:不能实时的持久化保存数据,在两次生成快照的期间,实时的数据可能伴随着重启而丢失

RDB 文件使⽤特定⼆进制格式保存,Redis 版本演进过程中有多个 RDB 版本,兼容性可能有⻛险,不过这都可以解决。


🐼AOF

AOF类似于MySQL中的binlog,不是记录数据,而是记录用户的操作,把用户的操作追加到文件中,以独立日志的方式记录每次写命令,重启时再重新执行 AOF ⽂件中的命令 达到恢复数据的目的。

✅如何打开AOF选项

AOF默认是关闭的,RDB默认是开启的。如果开启AOF,我们需要在配置文件设置配置appendonly yes。

aof的文件名,和rdb所在的目录是一致的,也是可以供我们配置的。

开启了AOF,那么RDB就不再生效了 ,redis就默认加载aof文件到内存了,不再读取rdb内容,不加载rdb了这也就变相解决了,刚刚我们rdb文件损坏,启动不了服务器的问题(但是仅解决了启动不了,但是数据是空的,如果想恢复原本的数据,还得相办法恢复rdb文件,比如拿着相同的key,插入,保存aof文件中)。并且我们发现在/var/lib/redis/多了.aof文件

✅.aof文件是什么?

aof文件本质是一个文本文件,每次的指令操作,记录下来,如图:

✅引入了AOF,redis还快吗?

由于aof要实时写数据到硬盘,这么,又要写内存,又要写硬盘,如何保证效率呢?

redis为了保证效率,引入了AOF缓冲区

AOF机制并非让数据直接写到硬盘上,而是 写到一个内存缓冲区 (AOF缓冲区),积累 了一部分,再统一 写到硬盘上!这有点类似于我们之前学习的Linux中的文件内核缓冲区,通过减少写入次数来大大提高效率!

如图:

所有的写⼊命令会追加到 aof_buf(缓冲区) 中,AOF 缓冲区根据对应的策略向硬盘做同步 操作

随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的,什么是重写,下面会分享~

当Redis 服务器启动时,可以加载 AOF 文件进⾏数据恢复

🔎可是现在还有一个问题,就是既然是先写入内存缓冲区,那么也是内存啊,那么要是没电了,或者redis进程挂了,这不导致了内存数据丢了,还是导致了没有保存到硬盘上的数据丢失了?是的!所以说尽管比起ROB,但是也不是完美的,那如何做?尽可能最大程度的保证数据不丢失呗~

redis给我们提供了一些选项,可以让我们根据实际情况,做出取舍,这些选项就是关于缓冲区的刷新策略的。

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

刷新频率越低,性能影响越小,性能越好,同时数据的可靠性就越低。

我们可以从配置文件中来配置这些选项,配置文件中的选项:

其中always,刷新频率最高的,性能最低的,可靠性最高,具体为命令写入AOF缓冲区(aof_buff)后调用fsync同步,然后立马返回。我们知道fsync是将文件缓冲区立马刷新到外设的系统调用。如果这些数据很重要,才能配置always。

其中everysec,刷新频率低一些,性能还可以,可靠性也能保障,如果根据你的场景不知道选什么,那么就建议配置这个选项。具体为命令写⼊aof_buf 后只执⾏ write 操作,不进行fsync。每秒由同步进程进行sync。这样理论上最多丢失 1 秒的数据。

其中no,刷新频率最低的,性能最高的,可靠性最差。具体为命令写⼊ aof_buf 后只执⾏ write 操作,由 OS 控制 fsync 频率。如果数据的重要程度很低,才能这么配置


✅AOF的重写机制

当.aof的文件越来越大,这会影响到下次 redis的启动时间 !不过其实在redis写入aof文件的过程中,有一些操作是冗余的。比如:

因此,redis就存在一个机制,能够对.aof文件进行整理操作,这个整理就是在剔除 其中的冗余 操作,并且合并 一些操作,达到**aof文件"瘦身"**的效果。这个就是重写机制。

所以这样就能保证redis的重写机制只关注最终结果,中间过程就不重要了~因为你执行那么多命令,最后还是到最终结果了,因此,redis,只关注结果。

💮AOF重写的过程

如图所示

所以,重写的时候,不关心aof文件的数据,只关心内存的最终数据状态。内存中数据的状态,相当于已经把aof文件整理后的模样了。

此处子进程重写数据的过程,有点类似于RDB生成的一个镜像快照,只不过RDB是按二进程的形式组织数据生成的,这里是按文本形式生成的,本质上都是将内存中的数据记录到文件中

AOF 重写过程依旧可以手动触发和自动触发

手动触发:调用 bgrewriteaof 命令。

自动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定自动触发时

机。

🔎下面,有几个问题:

如果在执行bgrewriteof时, 此时redis正在执行AOF重写,会咋样?直接返回,不会再次重写。

如果在执行bgrewriteof时, 此时redis正在执行RDB生成快照,会咋样?此时,aof重写就会等待,等待rdb快照形成,再重写。

父进程为什么还要往旧的AOF文件中写呢?就像上图的3.1路径。能不能不写?不能!考虑在重写的过程中,服务器挂了,子进程的内存数据丢失了,但是新的aof文件不完整,所以,如果父进程不坚持往旧的AOF文件中写数据,重启就无法保证数据的完整性了!

下面我们来验证一下手动重写,确实向aof文件中写入了数据:

并且我们发现尽管我们没有手动重写,AOF机制确实帮助我们实时完成了数据的备份,只要我们有修改,.aof文件就会有改动。

我们打开.aof文件,

我们发现AOF文件中既有二进制数据,也有文本数据,这是怎么回事?其实AOF本来是按照文本形式来写入数据的,但是文本的形式,后续重启redis服务器加载时成本较大。

redis就引入了"混合持久化"的方式,结合了AOF和RDB的特点,按照AOF的方式,每一个请求/操作,都记录到文件中,在触发aof重写后,就把内存中的二进制数据按照rgb的格式写入到新的AOF文件中,后续再进行的操作,仍然按照aof的格式追加到文件末尾。

我们在配置文件中可以手动开启/关闭,混合持久化这个选项。

如果关闭,改成no,那么我们手动重写后,.aof文件中的数据就是文本形式了。记得修改了配置文件服务器要重启。

混合持久化结合了两者的特点,可以达到实时性的同时,还保证了加载时不至于那么慢!

我们上面也说了,如果既有RDB文件也有AOF文件,redis启动时,到底加载谁?加载AOF,因为AOF更全的数据,如图:

相关推荐
8Qi81 分钟前
Redis哨兵模式(Sentinel)深度解析
java·数据库·redis·分布式·缓存·sentinel
数据库小组4 分钟前
从业务库到实时分析库,NineData 构建 MySQL 到 SelectDB 同步链路
数据库·mysql·数据库管理工具·数据同步·ninedata·数据库迁移·selectdb
CDN36011 分钟前
CDN HTTPS 证书配置失败?SSL 部署与域名绑定常见问题
数据库·https·ssl
Chengbei1117 分钟前
一次比较简单的360加固APP脱壳渗透
网络·数据库·web安全·网络安全·系统安全·网络攻击模型·安全架构
寒秋花开曾相惜18 分钟前
(学习笔记)3.9 异质的数据结构(3.9.1 结构)
c语言·网络·数据结构·数据库·笔记·学习
mcooiedo28 分钟前
mybatisPlus打印sql配置
数据库·sql
wudl556634 分钟前
MySQL 8.0.42 Docker 开发部署手册
数据库·mysql·docker
xhuiting38 分钟前
MySQL专题总结(四)—— 高可用
java·数据库·mysql
kjmkq1 小时前
目工业级宽温SSD哪个品牌不掉盘最稳定?宽温环境下的稳定性性技术解析
数据库·存储
Predestination王瀞潞1 小时前
Java EE3-我独自整合(第二章:Spring IoC 入门案例)
数据库·spring·java-ee