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

Redis—持久化

持久化

mysql当中,有4个比较关心的特性,分别是原子性、一致性、隔离性和持久性。这里的持久性和持久化是一回事。我们该如何判断是否具有持久性呢?答案就是看重启进程或者主机之后,数据是否存在。当我们把数据存储在硬盘上是就是持久的,把数据存储在内存上,就是不持久的。
我们知道redis的特点就是效率高,这是因为它是一个内存型数据库,是把数据存储在内存当中的,但是这样做不到持久性,想要持久性,就需要存储在硬盘上。因此redis为了二者兼得,它在内存和硬盘上都存有数据,这样的两份数据,理论上是完全相同的,但是实际上可能存在一个小的概率差异,这取决于我们具体怎么进行持久化。
例如,当要插入一个新的数据的时候,就需要把这个数据同时写入到内存和硬盘当中,当查询某个数据的时候,可以直接在内存中读取。而硬盘当中的数据只是在redis重启的时候,用来恢复内存当中的数据,相当于备份了一份数据。这样做的代价就是消耗了更多的空间,一份数据存储了两遍,但是毕竟硬盘毕竟便宜,这样的开销并不会带来太多的成本。
Redis进行持久化处理的时候,有两种方式,分别是RDBAOF

  • RDB(Redis DataBase):定期备份。定期同步内存和硬盘中的数据。
  • AOF(Append Only File):实时备份。只要内存中的数据被修改,就立即同步到硬盘中。
    接下来,我们就来学习这两种持久化方法。

RDB

RDB定期的把我们Redis内存中的所有数据,都给写入硬盘当中,生成一个“快照”。“快照”就相当于某个案发现场,警察来了拉上警戒线,然后警察们开始拍照,记录现场,后续就可以根据这些照片,来还原出现场当时发生了什么。
Redis中的“快照”,就是给内存中当前存储的这些数据,赶紧拍个照片,生成一个文件,存在在硬盘当中,后续Redis一旦重启了,就可以根据“快照”把内存中的数据给恢复回来。这里的“定期”具体来说,有两种方式。第一种是手动触发,程序员通过reids客户端执行特定的命令,来触发快照生成,命令有如下两种:

  • save:执行save的时候,redis就会全力以赴的进行“快照生成”操作,此时就会阻塞redis的其它客户端的命令,跟执行keys *的后果一样。所以不建议使用save
  • bgsavebg就相当于background,并不会影响redis服务器处理其它客户端的请求和命令。原理就是redis使用的是“多线程”的方式,来完成并发编程。流程如下:
    请添加图片描述
  1. 首先判定当前是否已经存在其他正在工作的子进程。比如说现在已经有了一个子进程正在执行bgsave,此时就直接把当前的bgsave返回。
  2. 如果没有其他的工作子进程,就通过fork创建出一个子进程。fork 过程中⽗进程会阻塞,通过 info stats命令查看latest_fork_usec 选项,可以获取最近⼀次 fork 操作的耗时,单位为微秒。
  3. 子进程负责进行写文件,生成快照的过程。父进程继续接收客户端的请求,继续正常提供服务。
  4. 子进程完成整体的持久化过程之后,创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换。
  5. 子进程发送信号给父进程表示干完了,,父进程就会更新一些统计信息,子进程就可以结束销毁了。

如果Redis服务器中存储的数据特别多,内存消耗特别大,那么父进程创建子进程的时候,子进程要把父进程的所有数据拷贝一份,会不会有很大的性能开销呢?其实是很小的,fork在进行内存拷贝的时候,不是直接把所有的数据都拷贝一遍,而是利用“写时拷贝”的机制来完成的。如果子进程里的这个内存数据和父进程的内存数据完全一样,此时就不会触发真正的拷贝动作,而是父子进程共用一份内存数据。


第二种就是自动触发,在Redis配置文件中,设置一下,让每隔多长时间或者每产生多少次修改就进行触发。
我们需要在打开redis的配置文件,在root用户下运行vim /etc/redis/redis.conf命令。
请添加图片描述

dir后面的路径,就是redis生成的RDB文件存放的位置。在该目录下,会有dump.rdb文件,这个就是生成的快照文件。这个文件是通过二进制存储数据的,如果直接打开会看到一些乱码。这个二进制的文件,会把内存中的数据以压缩的形式,保存到dump.rdb文件中。虽然压缩 RDB 会消耗 CPU,但可以⼤幅降低文件的体积,⽅便保存到硬盘或通过网络发送到从节点,因此建议开启。
请添加图片描述

这个图片就显示了dump.rdb文件多久更新一次。保存文件的格式是:

save <seconds> <changes>

也就是说只要在seconds秒内达到了changes次修改,那么就会更新文件,例如save 300 10表示的是如果在300秒内修改了10次数据,那么会在300秒后进行更新。当然也可以修改这些数值,如果要修改的话,需要遵从基本的原则:生成一次rdb快照,这个成本是比较高的,不能让这个操作执行的太频繁。

RDB的优缺点

  • RDB 是⼀个紧凑压缩的⼆进制文件,代表 Redis 在某个时间点上的数据快照。非常适⽤于备份,全量复制等场景。比如每 6 小时执行 bgsave 备份,并把 RDB 文件复制到远程机器或者文件系统中(如 hdfs)⽤于灾备。
  • Redis 加载 RDB 恢复数据远远快于 AOF 的⽅式。
  • RDB ⽅式数据没办法做到实时持久化 / 秒级持久化。因为 bgsave 每次运行都要执行 fork 创建⼦进程,属于重量级操作,频繁执行成本过高。
  • RDB ⽂件使用特定⼆进制格式保存,Redis 版本演进过程中有多个 RDB 版本,兼容性可能有风险。

AOF

AOF全称是append only file,类似于mysqlbinlong,会把用户的每个操作都记录到文件中。当开启aof的时候,rdb就不生效了,启动的时候不会再读取rdb文件内容。
redis重新启动的时候,就会读取aof文件中的内容,用来恢复数据,aof一般是关闭状态,可以通过修改配置文件来开启aof。把appendonly后的no改成yes就可以开启了。
请添加图片描述

修改完成后,要重启redis服务端:

service redis-server restart

重启完成之后,向redis中插入元素。
请添加图片描述

然后打开appendonly.aof文件。这个文件和dump.rdb文件在一起,位置是/var/lib/redis
请添加图片描述

AOF是一个文本文件,每次进行的操作,都会被记录到文本文件中,可以看到刚才的指令都在里面,这个文本文件通过一些特殊符号作为分隔符,来对命令的细节做出区分。
AOF模式下,又要写入内存,又要写入内存,那么会对性能有影响吗?实际上,并没有影响到redis处理请求的速度。原因是:

  1. AOF机制并非是直接让工作线程把数据写入到硬盘中,而是先写入一个内存中的缓冲区,积累一波后,再统一写入到硬盘中。比如,有100个请求,100个请求的数据一次写入到硬盘中,比分100次,每次写入一个请求要快的多。
  2. 硬盘上读写数据,顺序读写的速度是比较快的(不过跟内存相比要慢的多)。随机访问的速度是比较慢的。AOF是每次把新的操作写入到原有文件的末尾,属于顺序读写。

AOF的工作流程
请添加图片描述

  1. 所有的写入命令会追加到aof_buf(缓冲区)中。
  2. AOF缓冲区根据对应的策略向硬盘做同步操作。
  3. 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩目的。
  4. 当redis服务器启动时,可以加载AOF文件进行数据恢复。

AOF 过程中为什么需要 aof_buf 这个缓冲区?Redis 使⽤单线程响应命令,如果每次写 AOF ⽂件都直接同步硬盘,性能从内存的读写变成 IO 读写,必然会下降。先写⼊缓冲区可以有效减少 IO 次数,同时,Redis 还可以提供多种缓冲区同步策略,让用户根据自己的需求做出合理的平衡。

文件同步

将缓冲区的数据写入到硬盘的文件中叫做文件同步redis给出了一些选项,让程序员可以根据实际情况来决定怎么选择缓冲区的刷新策略,如果刷新频率过高,性能影响就越大,同时数据的可靠性就越高;如果刷新频率越低,性能影响就越小,数据的可靠性就越低。因此Redis 提供了多种 AOF 缓冲区同步⽂件策略,由参数 appendfsync 控制,
请添加图片描述

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

系统调用write和fsync说明:

  • write操作会触发延迟写机制。Linux在内核提供页缓冲区用来提供硬盘IO性能。write操作在写入系统缓冲区后立即返回。同步硬盘操作依耐于系统调度机制,例如:缓冲区页空间写满或达到特定时间周期。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
  • fsync针对单个文件操作,做强制硬盘同步,fsync将阻塞直到数据写入到硬盘。
  • 配置为 always 时,每次写⼊都要同步 AOF 文件,性能很差,在⼀般的 SATA 硬盘上,只能⽀持⼤约几百 TPS 写⼊。除非是非常重要的数据,否则不建议配置。
  • 配置为 no 时,由于操作系统同步策略不可控,虽然提高了性能,但数据丢失风险⼤增,除非数据重要程度很低,⼀般不建议配置。
  • 配置为 everysec,是默认配置,也是推荐配置,兼顾了数据安全性和性能。理论上最多丢失 1 秒的数据。

当AOF文件持续增长,体积会越来越大,会影响到redis下次启动的时间,因为redis启动的时候要读取AOF文件的内容,AOF文件记录了中间的过程,但是实际上redis在重新启动的时候只是关注最终结果。因此redis就存在一个机制,能够针对aof文件进行整理操作,这个整理就是能够去除其中的冗余操作,并且合并一些操作,达到给aof文件“瘦身”的效果

重写机制

随着命令不断写入 AOF,⽂件会越来越大,为了解决这个问题,Redis 引入 AOF 重写机制压缩文件体积。AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新的 AOF ⽂件。比如有如下操作:

set key 111
set key 222
set key 333
set key 444

每一次的set操作都会覆盖上一次的value,最后key里面就只有444这个数据,前面的操作相当于无效,如果redis真把每次操作都记录下来,那么就会浪费很多的空间,这个例子就相当于浪费了3/4的空间。
因此,redis才引入了重写机制,而重写后的文件体积变小,有如下原因:

  • 进程内已超时的数据不再写入文件。
  • 旧的AOF中的无效命令,例如del,hdel,srem等重写后将会删除,只需要保留数据的最终版本。
  • 多条写操作合并为一条,例如lpush list a、lpush list b、lpush list c可以合并为lpush list a b c

重写之后的AOF文件一方面降低了硬盘空间占用,一方面可以提升启动Redis时数据恢复的速度。AOF 重写过程可以分为⼿动触发和⾃动触发:

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

重写流程如下:
请添加图片描述

  1. 执行AOF重写请求。
    如果当前进程正在执行AOF重写,请求不执行。如果当前进程正在执行bgsave操作,重写命令延迟到bgsave完成之后再执行。
  2. 父进程执行 fork 创建子进程。
  3. 重写。
    1)主进程 fork 之后,继续响应其他命令。所有修改操作写入 AOF 缓冲区并根据 appendfsync 策略同步到硬盘,保证旧 AOF 文件机制正确。
    2)子进程只有 fork 之前的所有内存信息,fork之后,新来的请求对内存造成的修改,子进程是不知道的,此时,父进程这里又准备了一个aof_rewrite_buf缓冲区,专门放fork之后收到的数据。子进程这边,把aof数据写完之后,会通过信号通知父进程,父进程再把aof_rewrite_buf缓冲区中的内容也写入到新AOF文件中,这样就可以用新的AOF文件代替旧的AOF文件了。
  4. 子进程根据内存快照,将命令合并到新的 AOF 文件中。
  5. 子进程完成重写。
    1)新文件写入后,子进程发送信号给父进程。
    2)父进程把AOF重写缓冲区内临时保存的命令追加到新AOF文件中。
    3)用新AOF文件替换老AOF文件。

混合持久化

当redis启动时,会根据RDBAOF文件中的内容,进行数据恢复。
请添加图片描述

为什么要实现混合持久化的方式呢?因为AOF是按照文本文件的方式来写入文件的,但是文本的方式写入文件,后续加载的成本是比较高的,所以才引入了“混合持久化”的方式,结合了rdbaof的特点。这个方式需要在配置文件中开启,修改完之后,需要重启服务器。
请添加图片描述
按照aof的方式,每一个请求操作,都会被记录到文件中,在触发aof重写之后,就会把当前内存的状态按照rdb的二进制格式写入到新的aof文件中,后续再进行的操作,仍然是按照aof文本的方式追加到文件后面。同时存在rdb快照和aof文件的时候,是以aof为主,rdb会被直接忽略。

http://www.xdnf.cn/news/14639.html

相关文章:

  • 【Redis】Redis的下载安装和配置
  • 221. 最大正方形
  • SpringCloud系列(37)--搭建SpringCloud Gateway
  • MySQL为什么默认引擎是InnoDB?
  • 深度学习入门--(二)感知机
  • 微信小程序中scss、ts、wxml
  • DEAPDataset的EEG脑电图数据(Emotion_Prediction)使用介绍【第一期】
  • 【请关注】实操mongodb集群部署
  • APISIX
  • 鸿蒙Next仓颉开发语言中的数据类型总结分享
  • Spring 容器核心扩展实战:Spring Boot中三大扩展问题解析
  • sql格式化自动识别SQL语法结构
  • 大塘至浦北高速:解锁分布式光伏“交能融合”密码,引领绿色交通革命
  • 掌握CIS基准合规性:通过自动化简化网络安全
  • 磐维数据库PanWeiDB V2.0-S3.1.1_B01集中式一主二备安装
  • 细谈QT信号与槽机制
  • 覆盖迁移工具选型、增量同步策略与数据一致性校验
  • Unity3D仿星露谷物语开发70之背景音乐
  • 内存泄漏和内存溢出的区别
  • 【机器学习深度学习】非线性激活函数
  • Linux零基础快速入门到精通
  • 学习记录:DAY33
  • 2025.6.24总结
  • 用 Python 打造立体数据世界:3D 堆叠条形图绘制全解析
  • HTML炫酷烟花
  • 微算法科技开发基于布尔函数平方和表示形式的最优精确量子查询算法
  • NLP基础1_word-embedding
  • 利用大型语言模型增强边缘云 AI 系统安全性
  • AI智能化高效办公:WPS AI全场景深度应用指南
  • qt常用控件--03