高频面试题——Redis 的 AOF 持久化
良心公众号
关注不迷路
01
AOF 持久化背景
在之前的高频面试题——Redis 的 RDB 持久化文章中,我们讨论了 Redis 的 RDB 持久化。由于 RDB 文件的写入并不是实时的,通过 RDB 文件仅仅能将数据库状态恢复到上一次 RDB 持久化的状态,在此之后所做的改动将会被丢失,因此,RDB 持久化比较适用于对数据丢失不敏感的场合。
为了应对对数据丢失比较敏感的场合,Redis 提供了另外一种持久化的方式,也就是本文要提到的 AOF 持久化。
02
AOF 持久化简介
与 RDB 持久化通过保存数据库中的键值对来记录数据库状态的方式不同,AOF 持久化是通过保存 Redis 服务所执行的写命令来记录数据库状态的。
03
AOF 持久化原理
AOF 持久化功能的具体实现可以分为:命令追加,文件写入与同步。
命令追加
当开启 AOF 持久化功能之后,Redis 服务器在执行完成一个写命令之后,会将该命令追加到 aof_buf 缓冲区末尾。
文件写入与同步
当命令追加到 aof_buf 缓冲区末尾,Redis 进程会调用 flushAppendOnlyFile 方法,然后根据配置来决定是否将 aof_buf 缓冲区中的内容写入和保存到 AOF 文件中。这个配置是由 redis.conf 中的 appendfsync 选项的值来决定的。
appendfsync 的值有如下三个选项:
always: 将 aof_buf 缓冲区中的所有内容写入并同步到 AOF 文件。
everysec (默认): 将 aof_buf 缓冲区中的所有内容写入到 AOF 文件,但是否同步 AOF 文件取决于和上次同步的时间间隔是否超过一秒钟。
no: 将 aof_buf 缓冲区中的所有内容写入到 AOF 文件,但不主动对其进行同步,具体何时同步 AOF 文件由操作系统决定。
appendfsync 这三种选项实际上就是对于 AOF 写入和同步的三种策略。
always 策略是最为严格的,每次 flushAppendOnlyFile 都需要对 AOF 文件进行同步,因此效率最低,但换来的是较高的数据安全性,如果系统宕机,仅仅会丢失当前事件循环中执行的写入命令。
everysec 策略的严格程度次之,对于 AOF 文件的同步是以一秒钟为间隔的,因此效率相对高一些,但也就会有数据安全性方面的损失,如果系统宕机,会丢失最近一秒钟执行的写入命令。
no 策略的严格程度最弱,在 执行 flushAppendOnlyFile 方法期间,不会对 AOF 文件进行同步,因此效率最高,但数据安全性方面最弱,同时,在操作系统对于 AOF 文件进行同步的时候,由于期间可能积攒了大量的命令,所以单次同步时间会比较长,同时,如果系统宕机,会丢失从上次同步之后的所有写入命令。
由于 AOF 文件中包含了重建数据库状态所需的所有写入命令 (不考虑数据丢失情况),因此,服务器只需要读取 AOF 文件并执行其中的命令,即可还原服务器之前的状态,这也就实现了 AOF 持久化。
04
AOF 文件重写
细心的小伙伴可能会发现一个问题,随着时间的流逝,写入命令的量可能是巨大的,这会导致 AOF 文件的不断膨胀,这不仅会占用更多计算资源,也会在使用 AOF 文件还原数据的时候耗费更多的时间。为了解决这个问题,Redis 提供了 AOF 文件重写功能。
值得注意的是,AOF 文件重写的分析对象并不是现有的 AOF 文件,而是当前的数据库状态。举个例子:
redis> RPUSH numbers 1
redis> RPUSH numbers 2
redis> RPUSH numbers 3
redis> LPOP numbers
在对 numbers 键执行上述命令时,服务器为了保存当前 numbers 键的状态,会在 AOF 文件中写入上述四条命令。但在进行 AOF 重写时,对 numbers 键的状态进行分析,只需要保存下面这一条命令即可:
RPUSH numbers 2 3
这就是 AOF 文件重写的基本原理,通过 AOF 文件重写,可以将针对于同一个键的众多命令进行合并,大大减少命令数量,从而达到节省资源和提高效率的目的。
值得注意的是,由于 AOF 文件重写方法可能会执行大量的写入操作,因此,执行该方法的线程会被阻塞,由于 Redis 服务器是单线程的,因此,由 Redis 服务器线程来执行 AOF 文件重写方法是不合适的。对此,Redis 的处理是将 AOF 文件重写方法放到子进程里进行执行,这样可以避免阻塞服务器进程。同时,子进程持有服务器进程的数据副本,从而避免了数据共享,在不必考虑加锁的情况下保证了数据安全。
05
数据一致性
那么问题又来了,子进程在执行 AOF 文件重写期间,服务器很有可能会接收到新的写命令,此时,如何保证子进程和服务器进程数据一致呢?Redis 为了解决这个问题,引入了 AOF 重写缓冲区的概念。当 Redis 服务器执行写命令之后,会同时将这个写命令发送到 AOF 缓冲区和 AOF 重写缓冲区。当子进程完成 AOF 重写之后,会向父进程发送一个信号,父进程接收到信号之后,会将 AOF 重写缓冲区中的内容写入到新的 AOF 文件中,并用新的 AOF 文件原子地覆盖掉旧的 AOF 文件。
综上所述,本文关于 Redis 的 AOF 持久化机制就总结到这里了。
欢迎关注【有理想的菜鸡】公众号,大家一起讨论技术,共同成长!
06
相关阅读
07
参考资料
《Redis 设计与实现》黄健宏 著
https://github.com/redis/redis
学习 | 工作 | 分享
👆关注“有理想的菜鸡”