宕机后,Redis如何实现快速恢复?( 二 )


宕机后,Redis如何实现快速恢复?

文章插图
 
这既保证了快照的完整性,也允许主线程同时对数据进行修改,避免了对正常业务的影响 。
到这里,我们就解决了对“哪些数据做快照”以及“做快照时数据能否修改”这两大问题:Redis 会使用 bgsave 对当前内存中的所有数据做快照,这个操作是子进程在后台完成的,这就允许主线程同时可以修改数据 。
现在,我们再来看另一个问题:多久做一次快照?我们在拍照的时候,还有项技术叫“连拍”,可以记录人或物连续多个瞬间的状态 。那么,快照也适合“连拍”吗?
可以每秒做一次快照吗?
对于快照来说,所谓“连拍”就是指连续地做快照 。这样一来,快照的间隔时间变得很短,即使某一时刻发生宕机了,因为上一时刻快照刚执行,丢失的数据也不会太多 。但是,这其中的快照间隔时间就很关键了 。
如下图所示,我们先在 T0 时刻做了一次快照,然后又在 T0+t 时刻做了一次快照,在这期间,数据块 5 和 9 被修改了 。如果在 t 这段时间内,机器宕机了,那么,只能按照 T0 时刻的快照进行恢复 。此时,数据块 5 和 9 的修改值因为没有快照记录,就无法恢复了 。
宕机后,Redis如何实现快速恢复?

文章插图
 
所以,要想尽可能恢复数据,t 值就要尽可能小,t 越小,就越像“连拍” 。那么,t 值可以小到什么程度呢,比如说是不是可以每秒做一次快照?毕竟,每次快照都是由 bgsave 子进程在后台执行,也不会阻塞主线程 。
这种想法其实是错误的 。虽然 bgsave 执行时不阻塞主线程,但是,如果频繁地执行全量快照,也会带来两方面的开销 。
一方面,频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环 。
另一方面,bgsave 子进程需要通过 fork 操作从主线程创建出来 。虽然,子进程在创建后不会再阻塞主线程,但是,fork 这个创建过程本身会阻塞主线程,而且主线程的内存越大,阻塞时间越长 。如果频繁 fork 出 bgsave 子进程,这就会频繁阻塞主线程了 。那么,有什么其他好方法吗?
如果我们对每一个键值对的修改,都做个记录,那么,如果有 1 万个被修改的键值对,我们就需要有 1 万条额外的记录 。而且,有的时候,键值对非常小,比如只有 32 字节,而记录它被修改的元数据信息,可能就需要 8 字节,这样的画,为了“记住”修改,引入的额外空间开销比较大 。这对于内存资源宝贵的 Redis 来说,有些得不偿失 。
到这里,你可以发现,虽然跟 AOF 相比,快照的恢复速度快,但是,快照的频率不好把握,如果频率太低,两次快照间一旦宕机,就可能有比较多的数据丢失 。如果频率太高,又会产生额外开销,那么,还有什么方法既能利用 RDB 的快速恢复,又能以较小的开销做到尽量少丢数据呢?Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法 。
简单来说,内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作 。这样一来,快照不用很频繁地执行,这就避免了频繁 fork 对主线程的影响 。而且,AOF 日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销 。如下图所示,T1 和 T2 时刻的修改,用 AOF 日志记录,等到第二次做全量快照时,就可以清空 AOF 日志,因为此时的修改都已经记录到快照中了,恢复时就不再用日志了 。
宕机后,Redis如何实现快速恢复?

文章插图
 
这个方法既能享受到 RDB 文件快速恢复的好处,又能享受到 AOF 只记录操作命令的简单优势,颇有点“鱼和熊掌可以兼得”的感觉,建议你在实践中用起来 。

【宕机后,Redis如何实现快速恢复?】


推荐阅读