除了AOF还有什么在宕机后让Redis快速恢复数据?
AOF应用的场景应当是:数据量不大。一般而言只要采用的不是always的持久化策略,对性能的影响也就不大。
但是,也正因为记录的是操作命令而不是实际的数据,所以,用AOF的方法进行故障恢复的时候,需要逐一把操作日志的方法都执行一遍。如果操作日志非常多,Redis就会恢复的很缓慢,影响到正常使用。当然这不是理想的结果。
于是我们需要一种既可以保证可靠性,又能在宕机时实现快速恢复的其他办法?
内存快照
所谓内存快照,就是指内存中的数据在某一个时刻的状态记录。这就类似于照片,当你给朋友拍照的是欧,一张照片就能把朋友记录下来。
对Redis来说,他实现类似照片的记录效果,就是把某一时刻的状态以未见的形式写到磁盘上。这样一来即使宕机,快照文件也不会丢失,数据的可靠性也就得到了保证。这个快照文件就称为RDB文件(Redis DataBase)。
与AOF相比,RDB记录的是某一时刻的数据并不是操作,所以,在做数据恢复时,我们可以吧RDB文件读入内存,很快地完成恢复。但内存快照也并不是最有选项。
缺点
我们需要考虑到两个问题:一、对哪些数据做快照?(关系到快照的执行效率问题)二、做快照时,数据还能被增删改吗?(关系到Redis是否被阻塞,能否同时处理请求。)
给哪些数据做快照
Redis的数据全都在内存中,为了提供所有数据的可靠性保证,他执行的是全量快照,也就是说,把内存中的所有数据都记录到磁盘中,这就类似于给100个人拍合影,把每一个人都排进照片里。这样做的好处是,数据完整。坏处就是全量数据越多,RDB文件就越大,往磁盘上写数据的时间开销就越大。
对于Redis而言,它的单线程模型就决定了,我们要尽量避免所有会阻塞主线程的操作,所以,针对任何操作,我们都会提一个灵魂之问:**“他会阻塞主线程吗?”**RDB文件的生成是否会阻塞主线程,这就关系到是否会降低Redis的性能。
Redis分别提供了两个命令来生成RDB文件,分别是save和bgsave。
- save:在主线程中执行,会导致阻塞;
- bgsave:创建一个子进程,专门用于写入RDB文件,避免了主线程的阻塞,这是Redis RDB文件生成的默认配置。
我们可以选择bgsave命令执行全量快照,这样避免了对Redis的性能影响。接下来我们要关注的问题是:在对内存数据做快照时,这些数据还能被改动吗?如果不能改动,一位着Redis所有写操作都得等到快照完了才能执行,性能下降严重。
为了拍快照而暂停写操作这是Redis肯定不允许的,所以到了这个时候Redis就会借助操作系统提供的写时复制技术(Copy-On-Write,COW),在执行快照的同时,正常处理写操作。
简单来说,bgsave子进程是由主线程fork生成的,可以共享主线程的所有内存数据。bgsave子进程运行后,开始读取主线程的内存数据,并把它们写入RDB文件。此时,如果主线程对这些数据也都是读操作(例如途中的键值对A),那么,主线程和bgsave子进程互不影响。但是如果主线程要修改一块数据(例如途中的键值对C),那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave子进程会把这个副本数据写入RDB文件,而在这个过程中,主线程仍然可以直接修改原来的数据。
这即保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响。
可以每秒做一次快照吗?
虽然bgsave执行时不阻塞主线程,但是,如果频繁的执行全量快照,也会带来两方面的开销。
一方面,频繁将全量数据写入磁盘,会给磁盘带来巨大的压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环。
另一方面,bgsave子进程需要通过fork操作从主进程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork这个创建过程本身会阻塞主线程,而且,主线程的内存越大,阻塞的时间越长。如果频繁fork出bgsave子进程,这就会频繁阻塞主线程了。
为了解决上面提到的问题,可以采用增量快照,就是,做了一次全量快照后,后续的快照只对修改的数据进行快照记录,这样就可以避免每次全量快照的开销。
至此,虽然与AOF比,快照的恢复速度快,但是,快照的频率不好把握,如果频率太低,两次快照间一旦宕机,就可能会有比较多的数据丢失,那么,还有什么办法既能利用RDB的快速恢复,又能以较小的开销做到尽量少丢数据呢?Redis 4.0中提出了一个混合使用SOF日志和内存快照的方法。简单来说,内存快照以一定频率执行,在两次快照期间,使用AOF日志记录这期间的所有命令操作。