Redis进阶知识03-AOF和RDB实现原理

由 Geooo 发布于 February 16, 2022

Redis持久化

AOF(Append Only File)

AOF是如何实现的

AOF是 “写后日志” , 意思是先执行命令,把数据写入内存中,然后再记录日志,写进磁盘。(如下图)

传统数据库的日志,例如 redo log(重做日志),记录的是修改后的数据,而 AOF 里记录的是 Redis 收到的每一条命令,这些命令是以文本形式保存的。

因为AOF是在命令执行之后再记录的日志,并不会阻塞当前的写操作。(都是在主线程中操作)

Redis 收到“set testkey testvalue”命令后记录的日志为例,看看 AOF 日志的内容。其中,“*3”表示当前命令有三个部分,每部分都是由“$+数字”开头,后面紧跟着具体的命令、键或值。这里,“数字”表示这部分中的命令、键或值一共有多少字节。例如,“$3 set”表示这部分有 3 个字节,也就是“set”命令。

AOF三种写回(刷盘)机制

  • Always机制:”同步写回”,Redis在命令执行后立刻将日志写回磁盘。
  • EverySec机制:”每秒写回”, 每个写命令执行完后,先把日志写在AOF文件缓冲区中,1秒后将缓冲区的内容写入磁盘。
  • No机制:”操作系统控制写回”,每个写命令执行完后,先把日志写在AOF文件缓冲区,由操作系统决定什么时候将缓冲区的内容写入磁盘。

针对三种的写回机制优缺点如下图:

AOF重写机制

随着Redis执行命令越来越多,AOF日志文件则会越来越大,随之产生性能问题

  • 1.系统本身对文件大小存储是有限制的,无法保存过大的文件
  • 2.当AOF日志文件过大时,对AOF文件后继续追加效率也会变低
  • 3.Redis发生故障时,AOF日志恢复需要一条一条指令地恢复,若AOF日志过大则恢复时间也会变长

AOF重写机制原理:AOF文件是以追加的方式逐一记录接收到的写请求,当同一个key被多个写命令修改时,AOF会记录相对应多条写命令。在重写的时候,Redis会根据这个key的最新状态生成一条对应最新的写命令。

例子如下:

AOF重写会阻塞吗?

和 AOF 日志由主线程写回不同,重写过程是由后台子进程 bgrewriteaof 来完成的,这也是为了避免阻塞主线程,导致数据库性能下降。

一份拷贝,两份日志

一份拷贝: Redis主线程会fork出子线程 bgrewriteaof ,此时Redis主线程会复制一份内存给fork出来的 bgrewriteaof 子线程,这里包含主线程内存中最新的写命令数据,bgrewriteaof子线程在不影响主线程的前提下逐一拷贝写命令到重写日志中。

两份日志

  • AOF日志:在重写的过程中,Redis主线程仍然还会处理写命令,并把写命令写在 AOF日志的缓冲区中,即时此时宕机了,AOF日志还是完整的。
  • AOF重写日志:bgrewriteaof子线程会把新增的写入命令同步到 AOF的重写日志 的缓冲区,等到拷贝数据的所有操作记录重写完成后,这些在重写日志缓冲区中的新增命令也会写入新的日志中。

最后新的AOF日志文件生成后就会代替原本的AOF日志文件。(AOF重写具体流程实现如下图)

RDB(Redis DataBase)

针对Redis故障恢复时,AOF时一条指令一条指令地把操作日志都执行一遍,恢复效率特别慢,Redis还提供一种以内存快照的模式的持久化。

Redis的数据都在内存里,为了防止宕机等一系故障导致的数据丢失(可靠性),RDB执行的是全量快照,将某一时刻Redis中的内存数据全部以快照形式存储起来。

Redis提供了两个命令来生成RDB文件,savebgsave

  • save:在主线程中执行,会阻塞主线程。
  • *bgsave:fork出一个子进程,让子线程在后台将内存数据专门写入RDB文件,避免阻塞主线程 (bgsave是Redis生成RDB文件的默认配置)

RDB是如何运行的?

  1. 由于 bgsave子进程是由主线程fork出来的,可以共享主线程中的所有内存数据,bgsave子进程运行后,开始读取主线程内存中的数据,并把它们写入到RDB文件。
  2. 在bgsave子进程在写RDB文件的同时,主线程中发生了数据的写命令操作,Redis就会借助操作系统提供的写时复制技术(Copy-On-Write),将要修改的数据复制一份副本,Redis直接修改副本,bgsave就可以把原来的数据写入RDB文件。(快照期间修改数据如下图) 写时复制机制保证快照期间数据可修改
  3. 由与RDB不可能频繁地进行全量快照,因此Redis采用了 增量快照 模式进行存储两次快照间的修改数据,它记录了哪些数据哪个时刻被修改了,需要使用额外的元数据空间去存储数据的改动,带来额外的空间开销。

频繁全量快照带来的性能损耗

  1. 频繁将全量数据写入磁盘,会给磁盘带来巨大的压力,多个快照竞争有限的磁盘,导致前一个快照还没结束,后一个快照就开始写入,容易导致恶行循环。
  2. 另一方面,fork bgsave子进程的这个过程需要阻塞主线程,会给Redis带来性能损耗,若Redis主线程内存越大,阻塞时间越长。如果频繁fork子进程就会频繁阻塞主线程,对Redis非常不友好。

AOF与RDB混合模式

Redis4.0中提出一个 混合使用RDB内存快照与AOF日志的持久化方法,简单来说就是内存快照以一定的频率进行,两次全量快照之间,使用AOF来记录之间的命令操作,在下一次快照完成的时候即可删除上一次的AOF日志。 如下图所示,T1 和 T2 时刻的修改,用 AOF 日志记录,等到第二次做全量快照时,就可以清空 AOF 日志,因为此时的修改都已经记录到快照中了,恢复时就不再用日志了。

这种方法即享受了RDB快速恢复的好处,也享受到AOF只记录简单的操作命令优势。