RDB内存快照:Redis如何实现快速恢复数据

RDB内存快照:宕机后,Redis如何实现快速恢复数据

问题聚焦:

  • AOF方法进行故障恢复的时候,需要逐一把操作日志执行一遍,如果操作日志非常多,Redis恢复缓慢。如何在宕机时快速恢复Redis?

之前我们分析了Redis避免数据丢失的AOF方法,这个方法的缺点就是如果操作日志非常多,Redis恢复缓慢。这时就需要使用Redis持久化的另一种机制:内存快照(RDB,Redis DataBase)。所谓内存快照,就是指内存中的数据在某一个时刻的状态记录。这就类似于照片,当你给朋友拍照时,一张照片就能把朋友一瞬间的形象完全记下来。

对Redis来说,实现类似照片记录效果的方式,就是把某一时刻的状态以文件的形式写到磁盘上,也就是快照。

和AOF相比,RDB记录的是某一时刻的数据,并不是操作,所以在做数据恢复时,我们可以直接把RDB文件读入内存,很快地完成恢复。

但但内存快照也并不是最优选项,我们还要考虑两个关键问题:

  • 对哪些数据做快照?这关系到快照的执行效率问题;
  • 做快照时,数据还能被增删改吗?这关系到Redis是否被阻塞,能否同时正常处理请求。

下面主要对这两个问题进行分析

给哪些内存数据做快照?

Redis的数据都在内存中,为了提供所有数据的可靠性保证,它执行的是全量快照,即把内存中的所有数据都记录到磁盘中。

当然这样做的好处是一次性记录了所有数据,但会花费很多时间。

对于Redis而言,它的单线程模型就决定了,我们要尽量避免所有会阻塞主线程的操作,所以我们需要思考,RDB文件的生成是否会阻塞主线程,这就关系到是 否会降低Redis的性能。

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

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

此时我们就可以通过bgsave命令来执行全量快照,这既提供了数据的可靠性保证,也避免了对Redis的性能影响

快照时数据能修改吗?

首先我们要明白在做快照时也不希望数据“动”,也就是不能被修改。

  • 假设内存数据量是4GB,磁盘的写入带宽是0.2GB/s,简单来说,至少需要20s(4/0.2 = 20)才能做完。如果在时刻t+5s时,一个还没有被写入磁盘的内存数据A,被修改成了 A’,那么就会破坏快照的完整性,因为A’不是时刻t时的状态。

但是,如果快照执行期间数据不能被修改,Redis就不能处理对这些数据的写操作,那无疑就会给业务服务造成巨大的影响。

当然由于前文说到bgsave避免阻塞,我们这里要区分清楚避免阻塞和正常处理写操作并不是一回事。

  • 此时,主线程的确没有阻塞,可以正常接收请求,但是,为了保证快照完整性,它只能处理读操作,因为不能修改正在执行快照的数据。

  • 为了快照而暂停写操作,肯定是不能接受的。所以这个时候,Redis就会借助操作系统提供的写时复制技术 (Copy-On-Write, COW),在执行快照的同时,正常处理写操作。

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-grob9lyR-1618156402973)(C:\Users\cfs\Desktop\redis11.png)]

这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。

自此,我们就解决了对“哪些数据做快照”以及“做快照时数据能否修改”这两大问题:Redis会使用 bgsave对当前内存中的所有数据做快照,这个操作是子进程在后台完成的,这就允许主线程同时可以修改数据。

那么考虑另一个问题:多久做一次快照?我们在拍照的时候,还有项技术叫“连拍”,可以记录人或物连续多个瞬间的状态。那么,快照也适合“连拍”吗?

多久做一次快照?可以每秒做一次快照吗?

由上文我们知道每次快照都是由bgsave子进程在后台执行,也不会阻塞主线程。那为了在宕机时尽量少丢失数据,是不是可以使快照的间隔尽量小,是不是可以每秒做一次快照?

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

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

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

这时我们就可以使用另一种好方法:增量快照。

  • 在第一次做完全量快照后,T1和T2时刻如果再做快照,我们只需要将被修改的数据写入快照文件。但是这么做的前提是我们需要记住哪些数据被修改了。这需要我们使用额外的元数据信息去记录哪些数据被修改了,这也会带来额外的空间开销问题

在这里插入图片描述

  • 额外的空间开销问题:如果我们对每一个键值对的修改,都做个记录,那么,如果有1万个被修改的键值对,我们就需要有1万条额外的记录。而且,有的时候,键值对非常小,比如只有32字节,而记录它被修改的元数据信息,可能就需要 8字节,这样为了“记住”修改,引入的额外空间开销较大。这对于内存资源宝贵的Redis来说,有些得不偿失。

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

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

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

如下图所示,T1和T2时刻的修改,用AOF日志记录,等到第二次做全量快照时,就可以清空AOF日志,因为此时的修改都已经记录到快照中了,恢复时就不再用日志了。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值