上文提到了AOF日志,redis会将写命令持久化到AOF日志中,这样做的好处在于只有遇到写命令时才会记录该命令的日志,并且aof中提供了三种写入策略,一般会选用“允许数据有一点丢失,但不希望数据大量丢失并且不怎么影响redis性能”的everysec策略。
在redis写请求过大导致aof日志文件过大的场景中,redis还提供了aof的重写策略,通过子进程的形式来进行aof日志的重写,来合并重写前的日志。
但是上述的aof日志在redis用来恢复数据时也存在一些弊端,比如aof中主要记录的是写操作命令,并不是实实在在的数据,因此aof在进行故障恢复需要把命令全部执行一遍,如果aof日志命令很多,对redis数据恢复造成的直接影响就是会增加恢复时间,延长redis服务的不可用时间。
所以除了aof日志的方式,redis还提供了另一种方式:RDB日志 - 内存快照。
一、RDB日志 - 内存快照
这里的内存快照相当于把某一时刻的redis数据快照记录下来,写入到磁盘上。这样一来即使redis宕机,恢复后之间使用最新的快照数据进行恢复即可,不需要像aof一样持续写一遍命令。
二、RDB日志 - 如何使用RDB快照
为了提高数据的可靠性,我们可以使用RDB日志来快照redis的全量数据写入到磁盘中,这样做的好处在于,一次性就记录了所有数据。
但是使用这种方式也是存在弊端的,redis的全量数据越多,rdb日志文件就会越大,往磁盘上写数据也会花费更多的时间。
因此在redis中,rdb和aof日志其中一个最主要的区别就出来了,aof日志的写是通过主线程来写入缓冲区或者磁盘中的,而rdb日志的写是支持由redis主线程来创建出子进程来写入磁盘中。
· rdb快照写入模式
(1)主线程写入
save模式:在主线程中执行,但是会阻塞redis其他命令。
(2)异步写入
bgsave模式(默认配置):由主线程创建出一个子进程,异步来写入。不会阻塞redis其他命令。
三、RDB日志 - 如何保证数据的完整性
既然提到了rdb是一个快照日志,那么难免会出现在写入磁盘时未写入的数据被修改的情况,被修改后那么我们就不能得到那个时刻修改前的数据了。
因为redis的主线程还是在继续运行的,而又要求快照某一时刻的数据时需要保证数据的完整性。所以在不能阻塞主线程工作的情况下,rdb日志采用了两种方案来不阻塞主线程和保证数据的完整性。
1、上文中已提到,通过fork的方式,从主线程中生成一个bgsage的子进程来读取主线程中的值来写入磁盘。
2、对于需要快照某一时刻的数据时,在快照还没完成时的数据采用写时复制技术。
如果redis收到了读命令,那么和rdb的写入不冲突正常返回读取的数据即可。如果redis收到了写命令,并且正好处于rdb日志写入阶段。那么被修改的数据就会被复制一份出来,生成的是该数据对应的副本,主线程则正常修改数据,rdb日志则读取的是副本中的快照数据来替代读取主线程中的数据。
如下图所示:
以上的方式既保证了快照数据的完整性,又避免了阻塞主线程。
四、RDB日志 - 多久做一次快照
方案一:
为了尽可能的保证rdb日志中是最新的快照数据,提高rdb日志快照的频率,并且进行全量快照。
虽然每次快照都是有bgsave子进程在后台执行,不会阻塞主线程,但是调高rdb日志快照写入的频率也会带来很大的弊端。
第一点频繁将全量数据写入磁盘,会给磁盘带来很大的压力。
第二点redis的数据量很大,假设rdb全量数据的写入需要1s,而rdb日志写入间隔为1s,会导致前一个快照可能还没写完,后一个就开始了,造成多个快照竞争磁盘资源,造成恶性循环。
第三点rdb快照虽然是bgsave子进程在写入磁盘,但是bgsave是由主线程由fork创建出来的,fork会阻塞主线程。如果频繁fork就会频繁阻塞主线程。
方案二:
采用增量快照的方式。第一次全量快照后,后续的快照只对修改的数据进行快照记录,来减少每次全量快照的开销。但是这样会对redis的内存空间带来比较大的弊端。
由于增量快照的方式,我们需要使用额外的缓冲区空间来记录哪些数据被修改了需要进行快照记录,如果有1万个修改的键值对,那么就需要有1万个额外的记录。
引入的额外空间开销,对于redis内存资源来说有些得不偿失。
方案三:
redis4.0之后提出的思想,混合使用aof日志和rdb日志的方法。由于rdb快照是根据一定的频率来执行的,因此可以适当调高两次rdb快照之间的频率,在这两次之间的数据采用aof日志来进行记录。
这样一来,快照不需要很频繁的执行,避免了频繁fork对主线程的影响,而且aof日志只记录两次快照间的写操作,因此也不会出现aof文件过大的情况,减少重写开销。
最后,到了最新的rdb日志写入完成后,会清空老数据中的aof日志。
五、总结
本文主要分析了rdb日志文件的优势和弊端,以及使用中的建议。rdb的优势在于可以快速恢复数据,就好比redis的主从,在第一次从节点从主节点复制数据时,就不会通过采用aof日志的形式依次读取aof日志命令来使数据保存到从节点,只需要把主节点的数据通过快照的形式直接读入到从节点内存中即可。
快照的弊端在于频繁快照对于redis主线程的阻塞,所以还是要避免频繁快照,因此上文总结了使用快照的三种场景。场景三中的内存快照和aof的混合使用是一个很好的选择,并且可以保证数据不能丢失。
参考资料 - 《Redis核心技术与实战》(内存快照:宕机后,Redis如何实现快速恢复?)