【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更全的数据,如图:

相关推荐
burning_maple3 小时前
redis笔记
数据库·redis·笔记
oh LAN3 小时前
提升性能:数据库与 Druid 连接池优化指南
数据库·mysql
海底星光3 小时前
c# 生产者消费者模式之内存/redis队列实现
redis·c#
ABin-阿斌3 小时前
通过 Redisson防止数据重复创建
redis
砚边数影3 小时前
AI数学基础(一):线性代数核心,向量/矩阵运算的Java实现
java·数据库·人工智能·线性代数·矩阵·ai编程·金仓数据库
虹科网络安全3 小时前
艾体宝新闻 | Redis 月度更新速览:2025 年 12 月
数据库·redis·缓存
Linux-palpitate3 小时前
PostgreSQL单机部署
数据库·postgresql
engchina3 小时前
自然语言转 SQL 并不是“魔法”
数据库·人工智能·sql·text2sql·nl2sql·自然语言转sql
小陈phd4 小时前
langGraph从入门到精通(六)——基于 LangGraph 实现结构化输出与智能 Router 路由代理
android·网络·数据库