目录
AOF简介
除了RDB持久化功能之外,Redis还提供了AOF (Append Only File)持久化功能。
与快照持久化相比,AOF 持久化的实时性更好,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:
appendonly yes
与 RDB持久化通过保存数据库中的键值对来记录数据库状态不同,AOF持久化是通过保存 Redis服务器所执行的写命令来记录数据库状态的,如图
举个例子,如果我们对空白的数据库执行以下写命令,那么数据库中将包含三个键 值对:
RDB持久化保存数据库状态的方法是将msg、fruits、numbers三个键的键值对保 存到RDB文件中,而AOF持久化保存数据库状态的方法则是将服务器执行的SET、ADD、 RPUSH三个命令保存到AOF文件中。
被写入AOF文件的所有命令都是以Redis的命令请求协议格式保存的,因为Redis的命令请求协议是纯文本格式,所以我们可以直接打开一个AOF文件,观察里面的内容。
例如,对于之前执行的三个写命令来说,服务器将产生包含以下内容的AOF文件:
在这个AOF文件里面,除了用于指定数据库的SELECT命令是服务器自动添加的之外, 其他都是我们之前通过客户端发送的命令。
服务器在启动时,可以通过载入和执行AOF文件中保存的命令来还原服务器关闭之前 的数据库状态
AOF持久化的实现
AOF持久化功能的实现可以分为命令追加(append)、文件写入、文件同步(sync)三 个步骤。
命令追加
当AOF持久化功能处于打开状态时,服务器在执行完一个写命令之后,会以协议格式 将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾:
举个例子,如果客户端向服务器发送以下命令:
那么服务器在执行这个SET命令之后,会将以下协议内容追加到aof_buf缓冲区的 末尾:
以上就是AOF持久化的命令追加步骤的实现原理。
为什么执行完命令再写入
当 Redis 收到客户端修改指令后,会先进行参数校验、逻辑处理,如果没问题,就 立即 将该指令文本 存储 到 AOF 日志中,也就是说,先执行指令再将日志存盘。这一点不同于 MySQL
、LevelDB
、HBase
等存储引擎,如果我们先存储日志再做逻辑处理,这样就可以保证即使宕机了,我们仍然可以通过之前保存的日志恢复到之前的数据状态,但是 Redis 为什么没有这么做呢?
我甚至觉得没有什么特别的原因。仅仅是因为,由于AOF文件会比较大,为了避免写入无效指令(错误指令),必须先做指令检查?如何检查,只能先执行了。因为语法级别检查并不能保证指令的有效性,比如删除一个不存在的key。而MySQL这种是因为它本身就维护了所有的表的信息,所以可以语法检查后过滤掉大部分无效指令直接记录日志,然后再执行。
fsync
AOF 日志是以文件的形式存在的,当程序对 AOF 日志文件进行写操作时,实际上是将内容写到了内核为文件描述符分配的一个内存缓存中,然后内核会异步将脏数据刷回到磁盘的。
就像我们 上方第四步 描述的那样,我们需要借助 glibc
提供的 fsync(int fd)
函数来讲指定的文件内容 强制从内核缓存刷到磁盘。但 "强制开车" 仍然是一个很消耗资源的一个过程,需要 "节制"!通常来说,生产环境的服务器,Redis 每隔 1s 左右执行一次 fsync
操作就可以了。
Redis 同样也提供了另外两种策略,一个是 永不 fsync
,来让操作系统来决定合适同步磁盘,很不安全,另一个是 来一个指令就 fsync
一次,非常慢。但是在生产环境基本不会使用,了解一下即可。
AOF文件的写入与同步
Redis的服务器进程就是一个事件循环(loop), 这个循环中的文件事件负责接收客户端 的命令请求,以及向客户端发送命令回复,而时间事件则负责执行像serverCron函数这 样需要定时运行的函数。
因为服务器在处理文件事件时可能会执行写命令,使得一些内容被追加到aof_buf缓冲 区里面,所以在服务器每次结束一个事件循环之前,它都会调用flushAppendOnlyFile函 数,考虑是否需要将aof_buf缓冲区中的内容写入和保存到AOF文件里面,这个过程可 以用以下伪代码表示:
flushAppendOnlyFile函数的行为由服务器配置的appendfsync选项的值来决 定,各个不同值产生的行为如表
如果用户没有主动为appendfsync选项设置值,那么appendfsync选项的默认值 为everysec, 关于appendfsync选项的更多信息,请参考Redis项目附带的示例配置 文件redis.conf。
为了提高文件的写入效率,在现代操作系统中,当用户调用write函数,将一些 数据写入到文件的时候,操作系统通常会将写入数据暂时保存在一个内存缓冲区里面