redis 持久化

redis为什么要进行持久化?

我们都知道redis很快,其中一个重要的原因就是redis的所有操作都是在内存进行的,数据都是在内存中保存着的。但是对于对于内存来说,它的特点就是“掉电易失”,要是某一个我的程序突然崩溃了或者机房停电了,那么保存在redis中的数据也会跟着丢失,这一点无疑是制约了redis的可用性!
为了解决这个问题,保证redis的可用性,redis推出了持久化的方案,就是会对在内存中的数据在硬盘上进行备份处理,当redis重启的时候就可以冲这个备份文件中恢复原来的数据。

redis怎样进行持久化处理

目前来说,redis官方主要有两个持久化方案或者说是备份方案:

  1. RDB: 定期存储;redis每隔一段时间,就会将内存中的数据全量备份到硬盘,当redis重启的时候,只需要读取这个rdb文件即可恢复出丢失的数据。
  2. AOF: 实时存储;redis会将接收到的每一个写命令,都实时的存储在硬盘上的一个AOF文件中,当redis重启的时候只需要执行一下redis的AOF文件即可恢复数据。

接下来我们来详细的介绍一下这两个持久化机制;

RDB

RDB持久化,就是把当前进程的数据生成一个“快照”保存到硬盘的过程,触发RDB持久化的过程分为手动触发和持久化触发;

何为快照?
举个例子:就是在命案现场的时候,会有专门的警察拿一个照相机对着现场咔咔拍照?他们为什么要怎么做?当然是为了后期在分析案情的时候能够恢复出案发现场;redis中的“快照”也是同样的到来,当redis服务器启动RDB持久化过后,redis会记录一下这一瞬间的内存中的数据,就像“拍照片一样”,将这些数据定格住,然后在针对这些数据形成新的RDB文件,来替换掉旧的RDB文件;后期redis重启的时候,也就是根据当时“拍摄”的照片(RDB文件)来恢复redis中的数据。

RDB触发机制

1. 手动触发

如果我们想要手动触发redis的RDB持久化过程,那么我们可以通过以下的两个命令来进行:

  1. save命令: 阻塞当前redis服务器,直到RDB持久化过程完成,当内存中数据量比较大时,会造成长时间的阻塞,导致redis服务器无法及时响应来自客户端的请求,基本不会使用该命令;
  2. bgsave命令:当redis服务器接收到这个命令过后,redis服务器会使用fork系统调用来创建一个子进程,然后让这个子进程来完成RDB持久化的工作;在子进程完成RDB持久化工作期间,主进程不会受到干扰,仍然能够及时响应客户端的请求; 这是我们常用的命令;并且redis内部的所有涉及到RDB持久化操作的都是采用类似于bgsave命令这个流程。

下文,将详细讲述bgsave命令的工作流程;

2. 自动触发

除了手动触发之外,还有自动触发,并且自动触发才是最具有实际价值的,因为我们程序员不可能时时刻刻来监视redis 然后手动输入bgsave命令来完成持久化过程,都是交给redis内部自动完成判断,自动进行持久化的;

  1. 使用redis配置文件中的save配置,例如:“save m n” 表示在m秒内发生了n次修改,就自动触发RDB持久化过程:
    在这里插入图片描述
  2. 通过使用service redis-server stop、或redis客户端使用shutdown等这样正常的命令来使redis服务器正常关闭的话是可以自动触发redis的RDB持久化保存机制的。但是如果通过暴力的手段诸如:kill -9 的方式来杀掉redis进程,则不会触发redis的RDB自动持久化,有可能照成数据丢失。
  1. 通过service redis-server stop来关闭redis客户端
    在这里插入图片描述
    接着我们来看看这时候的rdb文件中的内容,并记住它:
    在这里插入图片描述

然后,我们接着又在客户端向服务端插入一些数据,然后再一次来观察rdb文件,我们发现这次的rdb文件与上次的rdb文件中的内容是一致的:
在这里插入图片描述

这说明我们本次插入的数据还未被持久化到rdb文件中去,我们紧接着退出redis客户端,然后使用service redis-server stop命令停止一下redis服务器,然后再来观察rdb文件中的内容:
在这里插入图片描述
我们发现在使用service redis-server stop 命令过后rdb文件中的内容,确实适合没插入数据之前的内容不一样了,这似乎也已经说明只要我们通过正常操作的方式来关闭redis服务器也是会触发redis的RDB持久化机制的;当然这只是从RDB文件上内容上来比较的,至于是不是真的将我们插入的数据进行了自动保存,我们重启redis服务器,然后通过客户端重新读取数据即可,读取得出来,那么说明redis确实自动完成了RDB持久化,反之,亦然:
在这里插入图片描述
通过实验结果,我们发现当我们正常关闭redis服务器的时候,redis确实会自动使用rdb持久化,来完成数据的存储;

  1. 通过kill -9来杀掉redis服务器,redis服务器不会触发rdb持久化存储;
    在这里插入图片描述
    先清空数据,并手动触发持久化,将本次结果持久化到硬盘;
    接着我们来观察此时的rdb文件:
    在这里插入图片描述
    然后我又在redis 客户端插入一些数据:
    在这里插入图片描述
    然后又来观察rdb文件:
    在这里插入图片描述
    似乎没有什么变化,这是正常的,因为我们这个时候并没有手动输入bgsave命令同时也不满足RDB自动触发的条件,因此我们此时插入的数据还在内存中,还没有被刷到硬盘中去,因此我们不能在插入数据过后立马看到rdb文件变化的原因。 接着我们使用kill -9 杀死redis服务器:
    在这里插入图片描述
    我们接着来观察rdb文件:
    在这里插入图片描述
    还是与原来的rdb文件内容一样,这似乎隐约的在说明异常终止redis服务器,确实不会自动触发RDB持久化,为了更加验证我们的猜想,我们通过登录redis客户端来获取一下刚才插入的数据即可:
    在这里插入图片描述
    我们发现,经过查询,并没有发现我们插入的数据的踪迹,这也说明像使用kill -9等暴力手段或者突然掉电这种这种方式来终结redis服务器,确实不能触发RDB持久化存储。
  1. 从节点第一次连接到主节点,主节点会自动触发RDB持久化,将主节点当前内存中的数据,形成一个快照,放在RDB文件中,然后再将这个RDB文件传输给从节点;

RDB持久化流程

bgsave是RDB持久化方式的主流存储方式,我们接下来就来详细介绍一下这个持久化方式的具体流程:
在这里插入图片描述

以用户手动触发为例(自动触发流程一样):

  1. 用户通过客户端输入bgsave命令,该命令会随着网络传输,传送到redis服务端;
  2. redis服务端接收到了来自客户端发送过来的bgsave命令。于是服务端开始检查当前redis服务器中是否已经存在了RDB持久化进程或AOF持久化进程正在进行持久化工作,如果存在,那么本次持久化命令则不生效。如果不存在,那么父进程则创建一个子进程来完成RDB持久化工作;
  3. 父进程完成子进程的创建过后,就不再过问持久化的工作了,而是继续响应来自客户端的命令。而对于子进程来说,它会对于此时的内存中的数据,也就是子进程被创建出来的那一刻的数据形成快照,然后将这些快照数据,写入到一个临时的RDB文件中。随着时间的推移,子进程终于写完了这个临时的新的RDB文件,于是,他便用这个新的RDB文件完成了对于这个旧的RDB文件的替换,这个文件替换过程是原子的,如果是第一次形成RDB文件,那么则无需完成替换,直接使用这个新的RDB文件即可,然后子进程发送信号来通知父进程,表示新的RDB文件已经生成好了;
  4. 父进程接收到来自子进程的信号过后,父进程会更新和统计新RDB文件的信息,并且关闭原来的RDB文件,并且重新打开RDB文件;

注意: 在Linux中,在文件已经被打开过后,再去磁盘上删除这个文件,并不影响程序通过已经打开的文件描述符去访问这个文件。

RDB持久化的总结

  1. 无论触发多少次RDB持久化,在硬盘上RDB文件都始终只有一份;
  2. RDB文件使用二进制的形式来进行存储的,并且在存储的时候redis默认采用LZF算法对生成的RDB文件进行压缩处理,压缩后的文件远远小于内存大小,虽然这很消耗cpu,但是降低了硬盘空间的占用率,同时在redis重启的时候,加载RDB文件恢复数据远远快于AOF文件;
  3. 对于形成的RDB文件不要乱改,不然可能造成RDB文件出错,导致redis服务器起不来;redis官方给我们提供了redis-check-rdb 这个命令来检查rdb文件的完整性;
  4. RDB文件中的数据并不是立马刷新的,也就是说并不是我们插入数据过后,这些数据就会被立马同步到rdb文件中,而是redis会根据自己的策略来同步已经写了的数据,或者说由我们开发人员手动的使用bgsave命令来触发RDB持久化机制,来完成数据的刷盘;
  5. 很显然,通过我们上诉对于RDB持久化机制的了解,我们发现RDB持久化是一个非常耗时的操作,因为RDB持久化持久化的是内存中的全量数据,是全量数据啊!不是部分!因此我们要实际开发中要避免频繁的使用RDB持久化机制;
  6. RDB持久化机制是个定时刷新策略,如果redis服务器在当前刷新点和下一个刷新点之间挂了,那么这之间的数据都会丢失。也就是说RDB持久化机制,没办法做到实时持久化/秒级持久化。针对这个问题redis提出了AOF的持久化方案;

AOF

AOF持久化:以独立日志的方式记录每次的“写命令”,重启时,redis只需要重新执行AOF文件中的命令即可达到恢复数据的目的;AOF的主要作用是解决了数据持久化的实时性,目前以及是redis持久化的主流方式。

开启AOF机制

redis默认情况下是开启RDB机制,而关闭AOF机制的,如果我们想要使用AOF机制,那么我们需要设置配置文件:“appendonly yes”,然后重启redis服务器;AOF文件默认文件名是appendonly.aof,保存路径与RDB文件一样。
在这里插入图片描述

AOF机制工作流程

下面我们来简单介绍一下,AOF的工作流程,虽然我们嘴上说AOF是一个实时刷新的策略,客户端发一个“写”命令过去,redis就会将这个“写命令”追加到AOF文件中,但是实际AOF的工作流程如下:
在这里插入图片描述

  1. 客户端发送过来一个写命令,redis除了会响应这个命令之外,还会处理将这个写命令,暂存到aof_buf缓冲区中;
  2. redis会根据自己的刷新策略,来定期的将aof_buf缓冲区中的数据以追加的方式、文本的方式刷新到硬盘上的aof文件中。
  3. 当用户重启的时候只需要执行读取AOF文件中的内容,然后执行里面的命令就能恢复出原来的数据;

针对上述的AOF工作流程,我们了解到以下几个点:

  1. AOF机制并不是我们想象的那样,来一个“写命令”,旧立马刷盘,而是先暂存与缓冲区中,然后结合自己的刷新策略来进行刷新;这样做有个好处就是,避免的频繁的IO,但是缺点也十分明显:就是这样的刷新机制,不就跟RDB一样了?在当前刷新点和下一个刷新点之间,如果程序崩溃,就会丢失缓冲区中的数据,数据可靠性性没有得到保障!第二点就是说,由于我们是追加的方式写AOF文件,随着时间的推移AOF文件会越来越大,会占用更多的磁盘空间。
  2. 针对于AOF缓冲区中数据容易丢失的缺点,redis提出了3中刷新策略,来降低数据丢失的可能性:
  1. always: 命令写入aof_buf缓冲区过后调用系统fsycn操作同步到AOF文件,fsync完成后线程返回;直白一点就是:写一个刷一个,这样无疑是可靠性最高的,但是性能确实最低的;
  2. everysec: 命令写入aof_buf后调用系统write操作,write完成后线程返回。fsync同步文件操作由专门的线程每秒调用一次;直白一点就是:每隔1s刷一次盘;可靠性和性能都中规中矩,理论上就算数据丢失,也只会丢失1秒内的数据(严格来说这种说法是不准确的)。
  3. no: 命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步,同步硬盘操作由操作系统负责,通常同步周期最长为30秒。直白一点就是: 每隔30刷一次盘,刷盘频率变低了,性能无疑是追高,但是可靠性最低。

上面的3种AOF缓冲区刷盘方式 ,由appendfsync配置项来控制,默认是everysec策略;

  1. 针对AOF刷新机制,会导致AOF文件越来越大这个缺点,redis又推出了AOF重写的机制,来实现,也就是说redis在特定条件下,会对硬盘上面的AOF文件进行重写;

重写机制

随着命令的不断写入AOF文件,AOF文件会变得越来越大,会占用更多的硬盘空间,虽然硬盘空间比较便宜,但是随着时间的推移,AOF文件种会出现比较多的冗余命令,这你能忍?eg:
在这里插入图片描述
要是把这些冗余命令合并了,那么不就节省了一部分硬盘空间了吗?同时如果AOF文件过大的话,是会拖慢redis重启的速度的!这你还能忍?
综上,这两个缺点,redis官方忍不住了,于是推出了这个重写机制
这个机制,会根据特定的条件,针对某一时刻内存中的数据特点,重写AOF文件;

重写机制的触发方式

于RDB持久化机制的触发方式一样,AOF重写机制的持久化方式分为:手动触发、持久化触发;

  1. 手动触发: 客户端直接输入命令bgrewriteaof 命令,该命令也是使用子进程来完成重写:
    在这里插入图片描述
    我们接着来看看AOF文件中的内容:
    在这里插入图片描述
    似乎只有一些二进制数据,并不是文本数据;实际上这些二进制数据是reids写的一些配置信息,我们不用管,接着我们来写入一些命令:
    在这里插入图片描述
    紧接着,我们来看看AOF文件:
    在这里插入图片描述
    我们可以看到,AOF持久化机制确实是以文本的方式来记录“写”命令的;
    然后我们接着来删除key1,然后新插入key3,看看AOF文件:
    在这里插入图片描述
    我们1可以看到,确实是以追加的形式来写入的“写”命令,然后紧接着我们来手动触发一下重写机制,看看重写过后的AOF文件:
    在这里插入图片描述
    在这里插入图片描述
    重写过后的内容与我们预期的一样的;

注意: 为了演示重写过后的效果,我将混合持久化关闭了的。(至于什么是混合持久化,下文再细讲);

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

在这里插入图片描述
auto-aof-rewrite-min-size: 表示运行AOF重写时文件最小体积,默认64MB;
auto-aof-rewrite-percentage:代表当前AOF文件空间和上一次重写后的AOF文件空间的比值;
自动触发时机=aof_current_size>auto-aof-rewrite-min-size&&(aof_current_size-aof_base_size)/aof_base_size>=auto-aof-rewrite-percentage

重写机制工作流程

当我们触发AOF重写的时候,redis内部做了那些事情?
下面我们将以手动触发的方式来进行讲解:
在这里插入图片描述

流程说明:

  1. 客户端发送bgrewriteaof命令到redis服务端;
  2. redis服务端接收到该命令过后,开始对该命令进行处理;首先redis服务器先检查一下,此时是否正在进行AOF重写,如果存在,那么直接返回;如果不存在,则执行本次AOF重写;如果此时redis服务器正在执行RDB持久化存储机制,那么aof重写操作就会等待,等待RDB持久化完成过后在进行重写;
  3. redis父进程开始进行AOF重写了,它会先创建出一个子进程出来,然后旧不问世事了,就继续去响应来自客户端的命令了,而对于子进程来说,它会对自己被创建出来的那一刻的数据,形成一个快照,然后根据这些快照,形成一个新的AOF文件。
  4. 在子进程重写AOF文件期间,又来了一些新的“写”命令,那么对于redis主线程来说,它不仅会将这个命令写入aof_buf缓冲区还会写入到aof_rewrite_buf缓冲区中去,当父进程接收到来自子进程发送过来的信号过后,父进程就知道子进程已经完成了AOF文件的重写工作,于是父进程就开始更新和统计新的AOF文件,然后再将aof_rewrite_buf缓冲区中的数据写入到新的AOF文件中,形成最终版本的AOF文件,然后用这个新的AOF文件替换旧的AOF文件;

重写机制的总结

  1. 当父进程fork出来子进程过后,再子进程进行AOF重写期间,又来了一些新的“写”命令,那么这时候,主进程不仅会将这些“写”命令写入到aof_buf缓冲区还会写入到aof_rewrite_buf缓冲区中去,那么在此期间,主进程将这些"写"命令写入到aof_buf是否还有意义?可不可以只写aof_rewrite_buf缓冲区?
    答: 不能不写aof_buf缓冲区,我们考虑一下极端情况;假设在子进程实现AOF重写期间,主进程只将这时候来到的“写命令”写入到aof_rewrite_buf缓冲区中去,那么对于旧的AOF文件来说,它里面的数据就停留在主进程fork之前,而如果恰巧在主进程将aof_rewrite_buf缓冲区中的数据写入到新AOF文件的时候掉电了,或者程序崩溃了,那么这时候aof_rewrite_buf缓冲区中的数据就会全部丢失,而这时候新的AOF文件中的数据是不完整的数据,如果重启的时候使用这个新的AOF问价来进行重启的话,那么会造成数据丢失的情况发生。而相反,我们在子进程进行AOF重写期间,主进程坚持维护aof_buf缓冲区,也就是坚持维护旧的AOF文件的话,那么即使出现上面这种突然掉电的情况,我们也可以利用这个旧的AOF文件来恢复出redis服务器的全部数据,保证了redis的可靠性。因此,我们说在子进程进行重写期间,父进程不得继续写aof_buf缓冲区。
  2. 通过前文的实验我们知道redis在写AOF文件的時候,是以文本的方式来来记录“写命令”的,而不是像RDB文件那样使用二进制的格式来存储数据,基于AOF文件这样的特点的话,那么势必会导致redis在加载AOF文件的时候相比于加载RDB文件可能会更耗时,可能会拖慢redis的启动速度,因此对于这个问题,redis官方又提出了“混合持久化”的方案。

混合持久化:
对于AOF文件加载成本高的缺点,redis引入了混合持久化的机制,混合持久化机制由配置项:
aof-use-rdb-preamble来控制的,默认情况下是开启的,这里为关闭状态,是前文为了做实验方便观察而设置的。
这个机制的工作原理大致如下:
对于用户输入的“写”命令,redis还是使用文本、追加的方式,写入到AOF文件中去,但是当触发重写机制过后,那么redis会将重写过后的内容以二进制的方式(也就是RDB文件的格式)写入到新的AOF文件中,如果重写过后,又来了一些新命令,这些命令还是以“文本”的方式追加到这个AOF文件中去,因此整个AOF文件中的文件格式大致如下:
在这里插入图片描述
在这里插入图片描述
为了后续的实验,现在我们来将混合持久化开启,然后重启服务器使配置项生效。紧接着,我们来开始我们的实验:

  1. 我们首先对数据库进行刷新,避免旧数据印象我们的操作,并且将本次操作重写到AOF文件中:
    在这里插入图片描述
  2. 现在,我们来插入一些数据看看:
    在这里插入图片描述
  3. 我们手动触发一下AOF重写机制,然后再来观察一重写过后的AOF文件:
    在这里插入图片描述
    我们发现确实是二进制的格式,那么说明当我们开启混合持久化存储过后,AOF重写机制重写的AOF文件是二进制格式,也就是RDB文件的格式,对于未开启混合持久化的redis服务器,在重写的时候还是利用文本的格式来重写AOF文件,但是无论是否开启混合持久化机制,对于正常的AOF持久化机制来说,对于“写命令"还是以文本的方式追加到AOF文件中:
    在这里插入图片描述
    这实际上也是验证了,当我们开启redis的混合持久化存储过后,AOF文件的格式会变为下图的样子:
    在这里插入图片描述

重启加载

当一个redis服务即开启RDB持久化也开启AOF持久化时,redis服务器是如何处理的,可以参考一下流程图:
在这里插入图片描述

  1. 从流程图中我们可以看到,从恢复数据方面的话,AOF优先级是高于RDB文件的,无论是否存在AOF文件,只要开启了AOF持久化机制,那么redis一律按照AOF文件来加载数据,因此当我们第一次启动AOF机制的时候要注意,由于我们是首次开启AOF机制,磁盘上没有AOF文件,但是redis在重启的时候,会创建一个空的AOF文件,然后基于这个空的AOF文件恢复数据,自然数据库里面就是什么数据也没有,这时候我们更希望redis去加载RDB文件,而不是AOF文件,这时候应该怎么办?
  1. 关闭AOF持久化机制,保留RDB持久化机制;
  2. 重启服务器,此时服务器加载的就是RDB文件;
  3. 确认数据恢复,在命令行热修改配置开启 AOF 持久化 config set appendonly yes;
  4. 等待 Redis 将内存中的数据写入 appendonly.aof 文件,此时 RDB 和 AOF 数据已同步;
  5. 停止 Redis,修改配置文件开启 AOF 持久化和 RDB 持久化;
  6. 启动 Redis,数据恢复和持久化配置完成。
  1. 在redis运行期间,AOF持久化机制和RDB持久化机制都开启的情况下,两个机制都会独立完成持久化工作,不会互相影响的彼此的操作,只有在恢复数据的时候AOF机制享有优先权,谁让AOF文件中存的数据完整性更好呢;
  • 6
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南猿北者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值