一文搞懂redis的存储机制AOF与RDB

RDB:

指定的时间间隔内将内存中的数据集快照写入磁盘,RDB持久化可以手动触发,也可以自动触发。

简单概述:RDB的备份是如何执行的

Redis会单独创建(fork) -个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件昔换上次持久化好的文件(dump.rdb)。

整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。

RDB 的缺点是最后一次持久化后的数据可能丢失。

接下来,来看看RDB的相关配置

关于RDB的config配置:

在redis.config文件中

vim redis.config

/

找到在这里插入图片描述

可以看到默认:(3600s)一小时内至少一个key改变,就进行持久化写入dump.rdb

在这里插入图片描述

上图的内容解释:

1:当redis无法写入磁盘的话,直接关掉redis写操作

2:用压缩存储

3:完整性检测

RDB数据恢复:cp dump2.rdb dump.rdb

可以将dump.rdb复制一份存储,改名为其他例如(dump2.rdb),若原本的dump.rdb消失可以将dump2.rdb换回名字dump.rdb

那么具体RDB是怎么进行的呢?

RDB:帮助干活的线程bgsave

对于Redis而言,它的单线程模型就决定了,我们要尽量避免所有会阻塞主线程的操作,所以,针对任何操作,我们都会提一个灵魂之问**:“它会阻塞主线程吗?”**RDB文件的生成是否会阻塞主线程,这就关系到是否会降低Redis的性能。

Redis提供了两个命令来生成RDB文件,分别是save和bgsave。

  • save:在主线程中执行,会导致阻塞;
  • bgsave:创建一个子进程,专门用于写入RDB文件,避免了主线程的阻塞,这也是Redis RDB文件生成的默认配置。

好了,这个时候,我们就可以通过bgsave命令来执行全量快照(也就是备份一次此时的数据),这既提供了数据的可靠性保证,也避免了对Redis的性能影响。

在执行快照的同时,如何正常处理写操作?

Redis就会借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。

简单来说,bgsave子进程是由主线程fork生成的,可以共享主线程的所有内存数据。bgsave子进程运行后,开始读取主线程的内存数据,并把它们写入RDB文件。

此时,如果主线程对这些数据也都是读操作,那么,主线程和bgsave子进程相互不影响。但是,如果主线程要修改一块数据,那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave子进程会把这个副本数据写入RDB文件,而在这个过程中,主线程仍然可以直接修改原来的数据。(也就是说读的时候不影响主线程和bgsave子进程。写的时候,bgsave子进程会copy一份数据副本,写入RDB文件,这个过程中任然可继续进行写操作)

是否可以一直连拍快照

要想尽可能恢复数据,t值就要尽可能小,t越小,就越像“连拍”。那么,t值可以小到什么程度呢,比如说是不是可以每秒做一次快照?毕竟,每次快照都是由bgsave子进程在后台执行,也不会阻塞主线程。

这种想法其实是错误的。虽然bgsave执行时不阻塞主线程,但是,如果频繁地执行全量快照,也会带来两方面的开销。

一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环。

另一方面,bgsave子进程需要通过fork操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork这个创建过程本身会阻塞主线程,而且主线程的内存越大,阻塞时间越长。如果频繁fork出bgsave子进程,这就会频繁阻塞主线程了。

考虑使用,增量快照,就是指,做了一次全量快照后,后续的快照只对修改的数据进行快照记录,这样可以避免每次全量快照的开销。

在第一次做完全量快照后,T1和T2时刻如果再做快照,我们只需要将被修改的数据写入快照文件就行。但是,这么做的前提是,我们需要记住哪些数据被修改了。但记录修改本身也很耗费内存资源。

RDB的巨大问题:如果两次快照间发生了宕机,数据丢失了怎么办?

虽然跟AOF相比,快照的恢复速度快,但是,快照的频率不好把握,如果频率太低,两次快照间一旦宕机,就可能有比较多的数据丢失。如果频率太高,又会产生额外开销,那么,还有什么方法既能利用RDB的快速恢复,又能以较小的开销做到尽量少丢数据呢?

Redis 4.0中提出了一个混合使用AOF日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用AOF日志记录这期间的所有命令操作。

这样一来,快照不用很频繁地执行,这就避免了频繁fork对主线程的影响。而且,AOF日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。

在这里插入图片描述

RDB的优势;

1:适合大规模的数据恢复

2:对数据完整性和一致性要求不高更适合(不在时间规定内可能不持久的储存)

3:节省磁盘内容

4:恢复速度块

5:可以快速恢复数据库,也就是只需要把RDB文件直接读入内存,

这就避免了AOF需要顺序、逐一重新执行操作命令带来的低效性能问题。

劣势:

1:fork的时候,内存中数据被克隆一份,大致2倍的膨胀性考虑

2:虽然redis在folk时使用了写时拷贝技术,但如果数据庞大时还是比较消耗性能

3:在备份周期在一定间隔时间做一次备份,如果redis意外down掉的话,就会丢掉最后一次快照后的所有修改

4、它拍的是一张内存的“大合影”,不可避免地会耗时耗力。虽然,Redis设计了bgsave和写时复制方式,尽可能减少了内存快照对正常读写的影响,

但是,频繁快照仍然是不太能接受的。而混合使用RDB和AOF,正好可以取两者之长,避两者之短,以较小的性能开销保证数据可靠性和性能。

动态停止RDB:redis-cli config set save""

save后给空值

AOF:

1、AppendOnlyFile的缩写,是一种增量备份模式,Redis默认不开启。

2、类似mysql数据库的binlog,记录所有的redis操作逻辑记录

appendonly yes #开启AOF

appendfilename "appendonly.aof" #设置备份文件名

appendfsync	everysec #每秒更新一次操作记录

3、Redis会在原有进程的基础上,重新fork一个子进程进行记录

4、日志形式记录写操作;以文件追加的方式记录(在同一个备份文件上追加操作记录)

以日志的形式来记录每个操作(增量保存),将redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初读取该文件重新构建数据,换言之,redis重启的话就可以根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复
在这里插入图片描述

appendponly 改为yes 重启redis后就可以使用

番外篇:怎么重启redis操作:

ps -ef | grep redis (查找redis相关的进程)

在这里插入图片描述

kill -9 15856

说明:

我们最常使用到的kill命令为:

kill PID
kill -9 PID

前者为请求目标进程退出,后者为强制杀死目标进程。

AOF日志是如何实现的?

Redis是先执行命令,把数据写入内存,然后才记录日志(这些操作都是在主线程中进行的所以会有阻塞的风险)

Redis使用写后日志这一方式的一大好处是,可以避免出现记录错误命令的情况。

除此之外,AOF还有一个好处:它是在命令执行后才记录日志,所以不会阻塞当前的写操作

在这里插入图片描述

我们以Redis收到“set testkey testvalue”命令后记录的日志为例,看看AOF日志的内容。其中,“*3”表示当前命令有三个部分,每部分都是由“$+数字”开头,后面紧跟着具体的命令、键或值。这里,“数字”表示这部分中的命令、键或值一共有多少字节。例如,“$3 set”表示这部分有3个字节,也就是“set”命令。

AOF的风险

首先,如果刚执行完一个命令,还没有来得及记日志就宕机了,那么这个命令和相应的数据就有丢失的风险。如果此时Redis是用作缓存,还可以从后端数据库重新读入数据进行恢复,但是,如果Redis是直接用作数据库的话,此时,因为命令没有记入日志,所以就无法用日志进行恢复了。

其次,AOF虽然避免了对当前命令的阻塞,但可能会给下一个操作带来阻塞风险。这是因为,AOF日志也是在主线程中执行的,如果在把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而导致后续的操作也无法执行了。

异常恢复:

如遇到AOF文件损坏,通过redis-check-aof --fix appendoly-aof进行恢复

在这里插入图片描述

**AOF同步频率设置:**redis.conf文件中

appendfsync always:始终同步,每次写完立刻记入,性能较差但数据完整性好

appendfsync everysec:每秒同步,本秒数据可能丢失

appendfsync no:不同步

在这里插入图片描述

AOF重写(大于64mb触发第一次重写,之后大于原本的两倍再重写)

AOF 持久化是通过保存被执行的写命令来记录数据库状态的,所以AOF文件的大小随着时间的流逝一定会越来越大;为了解决AOF文件体积膨胀的问题,Redis提供了AOF重写功能:

Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件,新旧两个文件所保存的数据库状态是相同的,但是新的AOF文件不会包含任何浪费空间的冗余命令,通常体积会较旧AOF文件小很多。

**运行原理:**AOF重写并不需要对原有AOF文件进行任何的读取,写入,分析等操作,这个功能是通过读取服务器当前的数据库状态来实现的。

如果当前列表键list在数据库中的值就为[“C”, “D”, “E”, “F”, “G”]。要使用尽量少的命令来记录list键的状态,最简单的方式不是去读取和分析现有AOF文件的内容,,而是直接读取list键在数据库中的当前值,然后用一条RPUSH list “C” “D” “E” “F” "G"代替前面的6条命令。

不过,虽然AOF重写后,日志文件会缩小,但是,要把整个数据库的最新数据的操作日志都写回磁盘,仍然是一个非常耗时的过程。这时,我们就要继续关注另一个问题了:重写会不会阻塞主线程?

AOF重写会阻塞吗?

和AOF日志由主线程写回不同,重写过程是由后台线程bgrewriteaof来完成的,这也是为了避免阻塞主线程,导致数据库性能下降。

我把重写的过程总结为“一个拷贝,两处日志”。

“一个拷贝”就是指,每次执行重写时,主线程fork出后台的bgrewriteaof子进程。此时,fork会把主线程的内存拷贝一份给bgrewriteaof子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。

“两处日志”又是什么呢?

因为主线程未阻塞,仍然可以处理新来的操作。

此时,如果有写操作,第一处日志就是指正在使用的AOF日志,Redis会把这个操作写到它的缓冲区。这样一来,即使宕机了,这个AOF日志的操作仍然是齐全的,可以用于恢复。

第二处日志,就是指新的AOF重写日志。这个操作也会被写到重写日志的缓冲区。这样,重写日志也不会丢失最新的操作。等到拷贝数据的所有操作记录重写完成后,重写日志记录的这些最新操作也会写入新的AOF文件,以保证数据库最新状态的记录。此时,我们就可以用新的AOF文件替代旧文件了
在这里插入图片描述

总结来说,每次AOF重写时,Redis会先执行一个内存拷贝,用于重写;然后,使用两个日志保证在重写过程中,新写入的数据不会丢失。而且,因为Redis采用额外的线程进行数据重写,所以,这个过程并不会阻塞主线程。

RDB和AOF之间的关系

1、优先级AOF>RDB

2、RDB和AOF之间是不会相互通信的

避免数据丢失:

如果直接在配置文件开启AOF开关,再重启redis

AOF在重启redis之后才开始生效。

重启之后,redis 首先加载AOF的备份文件(因为开了AOF开关),但是AOF是空的,所有Redis内存就被加载为空了。

这个时候,一旦触发或满足了RDB全量备份的条件,Redis会对整个内存数据库进行全量备份,并且覆盖掉原先的备份文件。

但是,这个时候,Redis的内存是空的,完整RDB之后,就把空内存库全量备份 覆盖了原有的RDB备份文件。

所以,内存空了,AOF还啥都没有,之前的RDB备份文件也被覆盖了。

总结一下,就是一波操作下来,重启之后的AOF占领了内存,内存又全量覆盖了之前重启之前全量备份的RDB,数据全没了。

最后,幸好redis的RDB的备份文件,每天都会备份一次,但是从上次备份到重启这段时间里的数据还是找不回来了。

解决方法:在线修改AOF开关:开启AOF增量备份模式,

#redis中查看AOF状态
127.0.0.1:7001> config get appendonly
1) "appendonly"
2) "no"
 
#在线设置AOF开关为yes
127.0.0.1:7001> config set appendonly yes
OK

AOF重写过程中的阻塞风险?

这里有两个风险。

风险一:Redis主线程fork创建bgrewriteaof子进程时,内核需要创建用于管理子进程的相关数据结构,这些数据结构在操作系统中通常叫作进程控制块(Process Control Block,简称为PCB)。内核要把主线程的PCB内容拷贝给子进程。这个创建和拷贝过程由内核执行,是会阻塞主线程的。而且,在拷贝过程中,子进程要拷贝父进程的页表,这个过程的耗时和Redis实例的内存大小有关。如果Redis实例内存大,页表就会大,fork执行时间就会长,这就会给主线程带来阻塞风险。

风险二:bgrewriteaof子进程会和主线程共享内存。当主线程收到新写或修改的操作时,主线程会申请新的内存空间,用来保存新写或修改的数据,如果操作的是bigkey,也就是数据量大的集合类型数据,那么,主线程会因为申请大空间而面临阻塞风险。因为操作系统在分配内存空间时,有查找和锁的开销,这就会导致阻塞。

总结,AOF持久化流程:

1:客户端的请求写命令会被append追加到aof缓冲区内

2:aof缓冲区根据aof持久化策略【always,everysec,no】将操作sync同步到磁盘中的aof文件

3:aof文件大小超过重写策略时,进行rewrite重写,压缩aof文件大小

4:redis服务重启会重新加载aof文件中的写操作达到数据恢复

优点:

备份更加稳健,丢失概率更小

可读的日志文本,可以处理误操作

劣势:

比起RDB占用更多磁盘空间

恢复备份速度更慢

每次读写都同步的话,有一定的性能压力

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值