Redis的持久化机制AOF与RDB(三)

Redis的持久化机制AOF与RDB

​ 因为Redis是内存数据库,意味着一旦服务器宕机或者其它原因引起的关机,内存中的数据将全部丢失。

为快速恢复,Redis也有相应的持久化策略。Redis持久化主要有两大机制,即AOF(appen only file)日志和 RDB快照

AOF日志

​ 在关系型数据库中,有个叫WAL的技术,即在实际记录数据前,会先把修改的数据记到日志文件中,以便故障时的数据恢复。而AOF是写后日志,即先执行命令将数据写入内存,然后才记录日志。

为什么Redis要先写内存再写日志呢?

AOF里记录的是Redis收到的每一条命令。先写内存再写日志,一方面是为了避免额外的检查开销,先写内存再写日志可以有效避免日志记录了错误的命令,这样在Redis恢复数据时才不会出错。另一方面,在命令执行后才记录日志,不会堵塞到当前的写操作。

但这样会存在两个问题。

  1. 如果刚执行完一个命令,还没有来得及记录日志就宕机了,那么这个命令和相应的数据就会有丢失的风险。对于将Redis用作数据库的来说,由于此命令没有被记录到日志中,那么就无法通过日志进行恢复了。当然对于将Redis用作缓存的话,可以从后端数据库中写回。
  2. 其次,写后日志虽然避免了堵塞当前命令的操作。但可能会对下一个操作带来堵塞风险。因为AOF日志也是在主线程中执行的,如果在写日志时磁盘压力大导致写盘慢,那么就会堵塞到下一个操作命令了。

针对上述两个问题,关键点在于AOF写日志的时机,AOF机制提供了三种写回策略。

写回策略,即啥时执行

​ AOF配置项appendfsync有三个可选值:

  • Always,同步写回:每个写命执行完,立即同步地将日志写回磁盘;
  • Everysec,每秒写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓存区中,每隔一秒把缓冲区中的内容写入磁盘;
  • No,操作系统控制的写回:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内存写回磁盘。

img

可以发现,这三种策略都是为了避免和减少上述数据丢失主线程堵塞 的问题。

三种策略都无法做到两全其美,像Always是偏向于避免问题1的,每次做同步写回都会涉及到写磁盘的操作,这样虽然可以很好减少和避免数据丢失的可能性,但是不可避免的会影响到主线程性能;

No在命令执行完后不直接写磁盘日志,在写完缓冲区后,就执行完毕了,这样很大程度上减少了主线程的堵塞,但是一旦宕机,处于缓冲区还没写入到日志的命令就会丢失了;

Everysec是在上述两项配置中做了折中,每秒的写回频率,可以减少同步写回对于主线程的堵塞,但也会丢失一秒内未落盘,即还在缓存区的数据。

AOF的重写机制

​ 随着接收的写命令越来越多,AOF文件也会越来越大。而AOF文件越来越大也会引发几方面的性能问题。

  1. 文件系统本身对文件大小有限制,无法保存过大的文件;
  2. 其次如果文件太大,之后再往里面追加命令记录的话,效率也会变低;
  3. 如果发生宕机,AOF中记录的命令要一个个被重新执行,用于故障恢复,如果文件太大,整个恢复过程就会非常缓慢,这就会影响到Redis的正常使用了。

为此必须采取一定的控制手段,也即采用AOF的重写机制。

AOF重写机制在重写时,Redis根据数据库现状创建一个新的AOF文件,将旧文件中的命令写入。但并非是逐一写入。因为AOF文件是以追加的方式写入的,同一个键值存在被多条写命令反复修改,AOF也会生成多条操作命令。为了节省空间及不必要的命令写入,在重写时只需要根据当前键值对的最新状态,为它生成对应的写入命令即可。

这样的操作虽然会缩小日志文件,但要把整个数据库的最新数据的操作日志都写回磁盘,仍然是一个非常耗时的过程。有个疑问是,这样的操作会堵塞到主进程吗?

答案是不会的,因为重写过程是由后台子进程bgrewriteaof来完成的。根据主进程fork出来的。

重写过程可以总结为,一次拷贝,两处日志。

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

两处日志,是指主线程正在使用的AOF日志,和AOF重写时新创建的日志。在重写时,主线程会将最新的操作命令写到AOF缓冲区中。等拷贝数据的所有操作记录重写完成后,重写日志记录的这些最新操作也会写入新的AOF文件,以保证数据库最新状态的记录。

img

疑问专区

  1. 在写回策略中,当采用Everysec或者No配置项后,会先把命令记录到内存缓冲区中,那么当内存缓冲区内存满了会怎么办?

答:查看源码发现,AOF会再重建一个内存缓冲区,如果缓冲区块过多,会有相应的日志警告。image

RBD快照

由于AOF日志保存的是操作日志,当用AOF方法进行故障恢复时,需要逐一把操作日志都执行一遍。如果操作日志非常多,Redis就会恢复很缓慢,影响到了正常使用。

RDB快照即是为了在故障恢复时,可以快速实现恢复的方法。

RDB快照即指的是,内存快照。就是指内存中的数据在某一时刻的状态记录。和AOF日志相对,RDB记录的是某一时刻的数据,并不是操作。所以在恢复数据时,可以直接将RDB文件读入内存,很快地完成恢复。

​ 做RDB快照时,需要关注几个点;

  • 给哪些内存做内存快照?
  • 快照时数据能修改吗?
  • 生成快照的频率如何?

给哪些内存做内存快照

Redis缓存的数据都在内存中,为了提供所有数据的可靠性保证,它执行的是全量快照。即将内存中的所有数据记录在磁盘中。好处是,记录了所有的数据,避免数据丢失。缺点是,当内存的全量数据越多时,将数据写入磁盘文件的时间开销就越高。

写入快照时,会堵塞到主线程吗?Redis提供了两个命令来生成RDB文件,分别是:

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

快照时数据能修改吗

在进行快照写入时,主线程可以进行写操作吗?对于做快照的操作来说,当然是希望数据是不变的,但如果在执行快照时不能修改数据,那么对于业务来说影响是很大的。

Redis 借助了操作系统提供的写时复制技术(Copy-On-Wirte,COW),在执行快照时,也能正常写入。

实现即是,因为通过bgsave命令产生的子进程是由主进程fork出来的,可以共享主线程的所有内存数据,bgsave子进程运行后,开始读取主线程的内存数据,并把它们写入RDB文件。

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

生成快照的频率如何

如何选择生成快照的频率,关系到数据丢失的范围磁盘写入的压力

生成快照的间隔时间越短,丢失的数据就越少。但如果间隔时间太短,即频率越快的话,会带来额外的开销,主要是两方面。

  • 频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没做完,后一个又开始做了,容易造成恶意循环;
  • 此外,bgsave子进程是由主进程fork出来的,而fork操作会堵塞到主线程,同时主线程的内存越大,堵塞时间就越长。如果频繁fork bgsave子进程,就会频繁堵塞到主线程了。

针对这个问题,可以采用增量快照
所谓增量快照,就是指,做了一次全量快照后,后续的快照只对修改的数据进行快照记录,这样可以避免每次全量快照的开销。

在第一次做完全量快照后,后续就需要记住哪些数据被修改了。

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

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

小结

理解了AOF日志和RDB快照的和概念后可以看出,AOF日志虽然能恢复数据,但当存在大量操作命令时,生成的文件会很大,恢复时也会影响到主线程。RDB快照记录了某个时刻的全量数据,相对AOF恢复速度快,但是快照生成的频率不好把控,如果频率太低,两次快照间一旦宕机,就可能有比较多的数据丢失。但频率太高,又会产生额外的开销。

混合使用RDB和AOF,正好可以取两者之长,避两者之短,以较小的性能开销保证数据可靠性和性能。

关于AOF和RDB的选择问题,可以参考下面的情况:

  • 数据不能丢失时,RDB和AOF混合使用是不错的选择;
  • 如果允许分钟级别的数据丢书,可以只使用RDB;
  • 如果值用AOF,优先使用everysec的配置选项,因为它在可靠性和性能之间取了个平衡。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值