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

redis的持久化

1. 持久化

        mysql事务的特性为原子性,一致性,持久性,隔离性。简单来说将数据存储在磁盘上意味着数据持久化,但是将数据存储在内存上不是持久化。当服务器或进程重新启动之后,数据是否依旧存在,即数据的持久化。

        redis是一个内存数据库,是将数据存储在内存中的,但是想要数据能够做到持久化就需要redis把数据存储到硬盘上,redis相对于mysql这种关系型数据库,其优势是效率高。

        redis在内存和磁盘上都存储数据,这两份数据理论上是完全相同的。当要插入一个新的数据的时候,就需要把这个数据同时写入到内存和硬盘,当查询某一个数据的时候,直接从内存中读取。硬盘的数据只是在reids重启的时候,用来恢复内存中的数据的。

        Redis实现持久化的时候,具体是按照什么样的策略采实现的呢,主要有下面两种方式。

        1.RDB => Redis DataBase       

         定期备份:每个月,把我电脑硬盘上的学习资料,整体的备份到这个备份盘中

       2.AOF => Append Only File  

        实时备份:只要我下载了一个新的学习资料,立即把这个学习资料往备份盘中考贝一份

1.1 RDB

        RDB:定期的把我们Redis内存中的所有数据,都给写入硬盘中,生成一个快照。Redis给内存中当前存储的这些数据,赶紧拍个照片,生成一个文件,存储在硬盘中。 后续Redis一旦重启了,(内存数据就没了),就可以根据刚才的“快照“,就能把内存中的数据给恢复回来。

        定期具体来说,又有两种方式:
1.手动触发
          程序通过redis客户端,执行特定的命令,来触发快照生成
          save:执行save的时候,redis就会全力以赴的进行“快照生成”操作,此时就会阻塞redis的其他客户端的命令,->导致类似于keys*的后果.一般不建议使用save。
          bgsave :bg=>background (后面在后台进行操作)  不会影响Redis服务器处理其他客户端的请求和命令
        Redis咋做到的上面的实现效果,此处redis使用的是“多进程”的方式,来完成的并发编程,即来完成的bgsave的实现~

2.自动触发
        在Redis配置文件中,设置让Redis每隔多长时间/每产生多少次修改就触发。

1.2 bgsave的处理流程

        1.判定当前是否已经存在其他正在工作的子进程比如现在已经有一个子进程正在执行bgsave,此时就直接把当前的bgsave返回。
        2.如果没有其他的工作子进程就通过fork这样的系统调用,创建出一个子进程来。

        Java进行并发编程,主要是通过“多线程”的方式,fork是inux系统提供的一个创建子进程的api(系统调用)如果是其他系统,比windows,创建子进程就不是fork(CreateProcess.)fork创建子进程,简单相暴,直接把当前的进程(父进程), 复制一份作为子进程,一且复制完成了。父子进程就是两个独立的进程就各自执行各自的了。

        一般会复制,pcb,虚拟地址空间(内存中的数据)文件描述符表。本来redisserver中,有若干变量,保存了一些键值对数据,随着这样的fork的进行,子进程的这个内存里也会存在和刚才父进程中一模一样的变量,因此,复制出来的这个“克隆体”(子进程)的内存中的数据,就是和“本体”(父进程)是一样的,接下来安排子进程去执行“持久化”操作,也就相当于把父进程本体这里的内存数据给持久化了。

        父进程打开了一个文件,fork之后,子进程也可以使用这个文件,就导致了子进程持久化写入的那个文件和父进程本来要写的那个文件是一个文件。

        如果当前Redis服务器中存储的数据很多,内存消耗很大,此时进行上述的复制操作,是否会有很大的开销

        3. 子进程负责进行写文件,生成快照的过程,父进程继续接受客户端的请求,继续正常的提供服务。

        4.子进程完成整体的持久化过程之后,就会通知父进程干完了,父进程就会更新一些统计信息,子进程就可以结束销毁了。

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

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

        dump.rdb文件,rdb持久化操作,是可以触发多次。当执行生成rdb镜像操作的时候,此时就会把要生成的快照数据,先保存到一个临时文件中,当这个快照生成完毕之后,再删除之前的rdb文件,把新生成的临时的rdb文件名字改成刚才的dump.rdb。

        redis配置文件路径:/etc/redis/redis.config;

        rdb文件中的数据,不是插入了数据,就会立即更新的。rdb的触发时机:1.手动(save,bgsave);2.自动(配置文件中,进行设置

下图中是redis触发rdb的条件。

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

        1、手动执行save&bgsave触发一次生成快照


        由于这里的数据比较少,执行bgsave瞬间就完成了.立即查看应该就是有结果的,如果以后数据多了,执行bgsave就可能需要消耗一定的时间.立即查看不一定就是生成完毕了,通过上述的操作,就可以看到,redis服务器在重新启动的时候,加载了rdb文件的内容,恢复了内存中之前的状态了。

        redis-cli,使用该指令进行redis客户端和服务器进行交互。

        2.插入新的key,不手动执行bgsave 。

        重启服务器,但是set的key4在重启之后依旧可以查询到。

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

        redis生成快照操作,不仅仅是手动执行命令才触发,也可以自动触发!
1)通过刚才配置文件中save执行M时间内,修改N次.....
2)通过shutdown命令(redis里的一个命令)关闭redis服务器,也会触发.(serviceredis-server restart)正常关闭~

3)redis进行主从复制的时候,主节点也会自动生成rdb快照,然后把rdb快照文件内容传输给从节点。

        如上所示,在unbtu中,使用kill-9 杀死redis线程之后,会有守护线程启动。

3. bgsave

Linux文件系统
文件系统典型的组织方式(ext4)主要是把整个文件系统分成了三个大的部分~
1.超级块(放的是一些管理信息)
2.inode区(存放inode节点.每个文件都会分配一个inode数据结构,包含了文件的各种元数据)类似于档案,存储文件的相关主要信息。

3.block区,存放文件的数据内容了 

        如果直接使用save命令,是不会触发子进程&文件替换逻辑,save就直接在当前进程中,往刚才的同一个文中写入数据了。

4.通过配置自动生成rdb快照            

        执行flushall也会清空rdb文件;

        对于redis来说,配置文件修改之后,一定要重新启动服务器,才能生效。如果想立即生效,也可以通过命令的方式修改。

        查看redis的进程,ps aux | grep redis;

5.如果把rdb文件,故意改坏了,会样?
          手动的把rdb文件内容改坏,然后一定是通过kill进程的方式,重新启动redis服务器,如果通过service redis-serverrestart重启,就会在redis服务器退出的时候,重新生成rdb快照就把咱们刚才改坏了的文件给替换掉了~
           刚才是改坏了,但是看起来好像redis服务器没有收到啥影响,还是能正常启动,还是能正确获取到key这里具体redis会咋样,取决于rdb文件坏了的地方在哪,刚才是改坏的地方正好是文件未尾,对前面的内容没啥影响,如果是中间位置坏了,可就不一定了~

        rdb文件是二进制的,直接就把坏了的rdb文件交给redis服务器去使用,得到的结果是不可预期的~可能redis服务器能启动,但是得到的数据可能正确也可能有问题也可能redis服务器直接启动失败....
        redis也提供了rdb文件的检查工具,可以先通过检查工具,检查一下rdb文件格式是否符合要求,工具目录在/usr/bin/

1.3 rdb的优点

        RDB是一个紧压缩的二进制文件,代表Redis在某个时间点上的数据快照。非常适用于备份 全量复制等场景 比如每6小时执行bgsave备份,并把RDB文件复制到远程机器或者文件系统中(如hdfs)用于灾备
        Redis加载RDB恢复数据远远快于AOF的方式。( RDB这里使用二进制的方式来组织数据   直接把数据读取到内存中,按照字节的格式取出来,放到结构体/对象中即可。AOF是使用文本的方式来组织数据的,则需要进行一系列的字符串切分操作)
        RDB方式数据没办法做到实时持久化秒级持久化。因为bgsave每次运行都要执行fork创建子进程 属于重量级操 频繁执行成本过高
        RDB文件使用特定二进制格式保存, Redis版本演进过程中有多个RDB版本,兼容性可能有风险。

         老版本的redis的rdb文件,放到新版本的redis中不一定能识别。一般来说,实际工作中,redis版本都是统一的。如果确实需要有一些“升级版本”的需求。确实需要升级,确实遇到了不兼容的问题,就可以通过写一个程序的方式,直接遍历旧的redis中的所有key,把数据取出来,插入到新的redis服务器中即可。
        RDB最大的问题,不能实时的持久化保存数据,在两次生成快照之间,实时的数据可能会随着重启而去失,这个问题就可以使用aof解决。

1.4 AOF

        类似于mysql的binlog,就会把用户的每个操作,都记录到文件中,当redis重新启动的时候,就会读取这个aof文件中的内容,用来恢复数据。aof默认一般是关闭状态,修改配置文件,来开启aof功能。

        当开启aof的时候,rdb就不生效了,启动的时候不再读取rdb文件内容了。

        Redis虽然是一个单线程的服务器,但是速度很快,重要原因,只是操作内存,引入AOF之后,又要写内存,又要写硬盘~~还能和之前一样快了嘛?实际上并没有影响到redis处理请求的速度:
        1.AOF机制并非是直接让工作线程把数据写入硬盘,而是先写入一个内存中的缓冲区(写硬盘的时候,写入硬盘数据的多少,对于性能影响没有很大,但是写入硬盘的次数则影响很大了),积累一波之后,再统一写入硬盘。

        2.硬盘上读写数据,顺序读写的速度是比较快的(还是比内存要慢很多)。随机访问则速度是比较慢的。AOF是每次把新的操作写入到原有文件的末尾.属于顺序写入~

        如果把数据写入到缓冲区里,本质还是在内存中,万一这个时候,突然进程挂了,或者主机掉电了,缓冲区中没来得及写入硬盘的数据是会丢的~
        redis给出了一些选项,让程用户根据实际情况来决定怎么取舍,缓冲区的刷新策略:

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

        一般默认的是everysec,意味着每一秒中刷新一次。

        AOF文件持续增长,体积越来越大,这样会影响到,redis下次启动的启动时间~redis启动的时候要读取aof文件的内容记录了中间的过程 ,实际上redis在重新启动的时候,只是关注最终结果,比如下面的aof文件中有很多冗余操作。

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

1.5 AOF重写的流程

        创建子进程fork,父进程仍然负责接收请求,子进程负责针对aof文件进行重写,重写的时候,不关心aof文件中原来都有啥,只是关心内存中最终的数据状态!
        子进程只需要把内存中当前的数据,获取出来,以AOF的格式写入到一个新的AOF文件中!内存中的数据的状态,就已经相当于是把AOF文件结果整理后的模样了

        此处子进程写数据的过程,非常类似于RDB生成一个镜像快照,只不过RDB这里是按照二进制的方式来生成的;AOF重写,则是按照AOF这里要求的文本格式来生成的. 这两者都是为了把当前内存中的所有数据状态记录到文件中!!

        子进程写新aof文件的同时,父进程仍然在不停的接收客户端的新的请求,父进程还是会写把这些请求产生的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本来是按照文本的方式来写入文件的,但是文本的方式写文件,后续加载的成本是比较高的,redis就引入了“混合持久化”的方式~~结合了rdb和aof的特点。
        按照aof的方式,每一个请求/操作,都记录入文件,在触发aof重写之后,就会把当前内存的状态按照rdb的二进制格式写入到新的aof文件中.后续再进行的操作,仍然是按照aof文本的方式追加到文件后面~


        当redis上同时存在aof文件和rdb快照的时候,以aof为主,rdb就直接被忍略了,因为aof中包含的文件比rdb更全。流程逻辑如下所示:

page在文件113左右。

1.6 信号通知父进程

        信号,可以认为是Linux的神经系统,进程之间的相互作用(也可以视为是进程间通信的一种手段),信号能表达的信息有限,并非像socket这样的方式可以传输任意的数据。

记录在案:汤老师语录,容忍对方的缺点,比对方有多少优点更重要。

ps:本文仅仅是redis学习中整理的笔记。

相关文章:

  • 详解 C++ 左值和右值对象 左值引用和右值引用
  • RabbitMQ报错:Shutdown Signal channel error; protocol method
  • 微信小程序-实现锚点跳转,页面加载后自动跳转、点击跳转到指定位置
  • ubuntu 设置允许root远程登录
  • c语言笔记 静态函数和递归函数
  • 基于PyTorch通信算子的分布式训练阻塞定位方法
  • emacs使用mongosh的方便工具发布
  • 为什么 JPA 可以通过 findByNameContaining 自动生成 SQL 语句?
  • The First项目报告:重塑 DeFi 流动性的革新者,ELX 即将登陆 The First
  • Vue 系列之:路由
  • 玩转python:通俗易懂掌握高级数据结构:collections模块之namedtuple
  • 【附JS、Python、C++题解】Leetcode面试150题(9)——三数之和
  • C语言基础知识04
  • 2025-03-10 学习记录--C/C++-PTA 习题11-4 字符串的连接
  • Mysql_DML
  • java中如何把json转化的字符串再转化成json格式
  • python画图文字显示不全+VScode新建jupyter文件
  • 《SQL性能优化指南:新手如何写出高效的数据库查询
  • C# 事件使用详解
  • CPT208 Human-Centric Computing 人机交互 Pt.1
  • 中国兼职设计师网/常德网站seo
  • 成都网站建设优惠活动/长春视频剪辑培训机构
  • 工信部网站 登陆/成都网站seo技巧
  • dedecms 做微网站/手机百度搜索引擎入口
  • 阿里云网站怎么做/免费推广引流软件
  • 信息管理网站开发的视频教程/引擎搜索有哪些