Chapter 11 - AOF 持久化
Created by : Mr Dk.
2020 / 06 / 05 15:50
Nanjing, Jiangsu, China
AOF (Append Only File) 通过保存 Redis 服务器所执行的写命令来记录数据库状态。在服务器启动时,可以直接载入 AOF 文件中保存的命令,还原服务器关闭之前的数据库状态。
Implementation of AOF
AOF 持久化功能由三个步骤实现:
- 命令追加
- 文件写入
- 文件同步
命令追加
服务器执行完一个写命令之后,会按照 AOF 的格式把被执行的命令 append 到服务器中的 aof_buf
缓冲区中。这个缓冲区被维护在服务器状态中:
struct redisServer {
// ...
sds aof_buf;
// ...
}
文件写入与同步
Redis 的服务器进程是一个 事件循环,在每一轮循环中,主要干了三件事情:
- 处理文件事件 (接收客户端命令并发送回复)
- 处理时间事件 (周期性定时事件)
- 刷新 AOF
刷新 AOF 的行为有三种 (默认使用 everysec,可以通过配置修改):
- always - 将 AOF 缓冲区中的所有内容 写入 并 同步 到 AOF 文件
- everysec - 将 AOF 缓冲区中的所有内容 写入 AOF 文件,如果距离上次同步超过 1s,则 同步 AOF 文件
- no - 将 AOF 缓冲区中的所有内容 写入 AOF 文件,但 不同步
文件的写入和同步有何区别?
为加快对文件的读写,OS 通常会在内存中为文件开辟高速缓冲。当缓冲被填满,或超过指定时限后,才将缓冲区的内容真正同步到磁盘上。效率的提升会带来安全问题 (如果计算机发生停机,内存中还未同步到磁盘上的数据将丢失)。OS 提供
fsync
和fdatasync
两个同步的函数,可以强制立刻将缓冲区中的数据写入到硬盘里。
以上三种 AOF 行为:
- always - 性能最差,但同时也是最安全的
- everysec - 是性能与安全性的折衷,最多只会丢失一秒钟内的命令
- no - 由 OS 控制何时同步,所以性能最好;但该模式的 单次同步时长 最长;若发生故障,将丢失从上一次同步开始的所有数据
AOF 文件载入与还原
Redis 会创建一个不带网络连接的伪客户端,然后依次执行 AOF 文件中的命令,就能够恢复数据库的状态。
AOF Rewrite
由于 AOF 是 append only 的,那么文件中的内容必然会越来越多。Redis 能够创建一个新的 AOF 文件,并在其中删除冗余命令,同时保持数据库状态不变,然后原子地替换旧的 AOF 文件。
从原理上来说,读取某个时刻的数据库状态,就能够替代之前一切对于数据库进行写操作的命令。在重写之后,AOF 文件中只包含还原当前数据库状态所必须的命令。因此,只需要使用 SADD
或 RPUSH
记录数据库的状态,就能压缩 AOF 文件。
在重写期间必然会产生大量的写入操作。为了不阻塞服务器主进程,AOF 重写应当由子进程在后台完成:
- 子进程 AOF 重写期间,服务器主进程可以继续接收请求命令
- 子进程带有服务器主进程的 数据副本,因此,在子进程写入数据期间,数据不会受到主进程的影响
仅剩的一个问题是,子进程在进行 AOF 重写期间,服务器主进程执行的命令没有被记录。为了解决这个问题,Redis 设置了一个 AOF 重写缓冲区,这个缓冲区只有在 AOF 重写期间才会被启用。在 AOF 重写期间,服务器主进程每执行一条命令,同时也需要在 AOF 重写缓冲区中记录。
当子进程完成 AOF 重写后,会向主进程发送一个信号。在主进程的信号处理函数中:
- 将 AOF 重写缓冲区中的内容加入到新的 AOF 文件中
- 原子地将新的 AOF 文件覆盖现有的 AOF 文件
整个 AOF 重写的过程中,只有在执行这个信号处理函数时,服务器主进程会被阻塞。使 AOF 重写对服务器性能造成的影响降到了最低。
怎么和 JVM 的 GC 过程有些类似呢...