简介

AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性。

类似 MySQL 的 Binlog 日志

写入与恢复原理:

image.png
image.png

使用 AOF

如何开启 AOF

开启 AOF 功能需要设置配置:CONFIG SET appendonly yes,默认不开启。

AOF 文件名通过 appendfilename 配置设置,默认文件名是 appendonly.aof。保存路径同 RDB 持久化方式一致,通过 dir 配置指定。

AOF 的工作流程

AOF的工作流程操作:命令写入 (append)、文件同步(sync)、文件重写(rewrite)、重启加载。

image.png

流程介绍:

  1. 所有写入命令会追加到 aof_buf 缓冲区
  2. 缓冲区针对不同策略,向硬盘做同步操作
  3. 随着 AOF 文件的增大,需要定期针对 AOF 文件进行重写,达到压缩的目的
  4. 当 Redis 重启服务时,可加载 AOF 文件进行恢复。

文件写入

AOF 命令写入的内容直接是文本协议格式。

例如 set bb 2 ,将会在 AOF 文件追加如下文本:

  1. *3
  2. $3
  3. set
  4. $2
  5. bb
  6. $1
  7. 2

解释:*3 为代表命令有三个关键词;$3/$2/$1 代表命令中对应的关键词字符长度

答疑:

  1. AOF 为什么直接采用文本协议格式?
    1. 文本协议具有很好的兼容性。
    2. 开启 AOF 后,所有写入命令都包含追加操作,直接采用协议格式,避免了二次处理开销。
    3. 文本协议具有可读性,方便直接修改和处理。
  2. AOF 为什么把命令追加到 aof_buf 中?
    1. Redis 为单线程响应命令,每次将 AOF 文件命令追加到硬盘,Redis 性能受限于硬盘负载。
    2. 先写入缓冲区 aof_buf 中,Redis 可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。

文件同步

文件同步的三种策略

Redis 提供三种 AOF 缓冲区同步文件策略,可通过 appendfsync 参数控制。

策略 策略说明
always 每条命令都同步到 AOF 文件:Redis 将命令写入 aof_buf 缓冲区后,调用系统 fsync 同步到 AOF 文件;fsync 完成后线程返回。
everysec
(默认值)
每秒一次将命令同步到 AOF 文件:Redis 将命令写入 aof_buf 缓冲区后,调用系统 write 操作,write 完成后线程返回。fsync 同步文件操作有专门的线程每秒调用一次。
no Redis 不执行 AOF 同步操作,由操作系统负责同步:Redis 将命令写入 aof_buf 缓冲区后,调用系统 write 操作,不对 AOF 做 fsync 同步。同步硬盘操作由操作系统负责,通常同步周期最长为 30 秒。

关于 write 和 fsync 的说明:

  • write 操作会触发延迟写(delayed write)机制。Linux 在内核提供页缓冲区用来提高硬盘IO性能。write 操作在写入系统缓冲区后直接返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区页空间写满或达到特定时间周期。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
  • fsync 针对单个文件操作(比如AOF文件),做强制硬盘同步,fsync 将阻塞直到写入硬盘完成后返回,保证了数据持久化。

除了writefsync,Linux 还提供了 syncfdatasync 操作

不同策略的优缺点

策略 优点 缺点
always 不会丢失数据 硬盘读写 IO 性能消耗大
PS:大多数硬盘扛不住
everysec
(默认值)
每秒一次将命令同步到 AOF 文件,减少硬盘读写 IO 负载;能兼顾性能和数据安全。 当系统宕机会存在 1 秒的数据丢失情况
no 提升性能 采用操作系统调度,同步周期不可控,数据安全性无法保证

重写机制

Redis 为减少命令的不断写入引起的 AOF 文件不断增大,引入了 AOF 的重写机制,来压缩文件体积

AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新 AOF 文件的过程。

AOF 重写除了可降低文件占用空间以外,还可以加速 Redis 快速加载 AOF 文件

体积变小

重写后的 AOF 文件变小的原因:

  • 进程内已经超时的数据不再写入文件。
  • 旧的 AOF 文件含有无效命令,如 del key1hdel key2srem keysset a111set a222 等。重写使用进程内数据直接生成,这样新的 AOF 文件只保留最终数据的写入命令。
  • 多条写命令可以合并为一个,如:lpush list alpush list blpush list c 可以转化为:lpush list a b c。为了防止单条命令过大造成客户端缓冲区溢 出,对于 listsethashzset 等类型操作,以 64 个元素为界拆分为多条。
原始命令 重写后命令 说明
del key1
hdel key2
无效(删除)的数据(命令)
set aaa
set bbb
无效的命令:语法错误等
set aaa 111
set aaa 222
set aaa 222 保留最终一个命令
lpush list a
lpush list b
lpush list c
lpush list a b c 合并命令

触发机制

  • 手动触发:直接执行 bgrewriteaof 命令
  • 自动触发:根据 auto-aof-rewrite-min-sizeauto-aof-rewrite-percentage 参数确定自动触发时机。
    • auto-aof-rewrite-min-size:表示运行 AOF 重写时文件最小体积,默认为64MB。
    • auto-aof-rewrite-percentage:代表当前 AOF 文件空间增长率,即当前文件空间 (aof_current_size)和上一次重写后 AOF 文件空间(aof_base_size)的比值。
    • 自动触发时机**(以下两条件必须同时满足)**:
      • aof_current_size > auto-aof-rewrite-min-size
      • (aof_current_size-aof_base_size)/ aof_base_size >= auto-aof-rewrite-percentage

其中 aof_current_sizeaof_base_size 可以在 info Persistence 统计信息中查看。

重写流程

image.png

流程说明:

  • 1、执行 AOF 重写请求。
  • 2、父进程执行 fork 创建子进程,开销等同于 bgsave 过程。
  • 3.1、主进程 fork 操作完成后,继续响应其他命令。所有修改命令依然写入 AOF 缓冲区并根据 appendfsync 策略同步到硬盘,保证原有 AOF 机制正确 性。
  • 3.2、由于 fork 操作运用写时复制技术,子进程只能共享 fork 操作时的内存数据。由于父进程依然响应命令,Redis 使用“AOF重写缓冲区”保存这部分新数据,防止新 AOF 文件生成期间丢失这部分数据。
  • 4、子进程根据内存快照,按照命令合并规则写入到新的 AOF 文件。每次批量写入硬盘数据量由配置 aof-rewrite-incremental-fsync 控制,默认为 32MB,防止单次刷盘数据过多造成硬盘阻塞。
  • 5.1、新 AOF 文件写入完成后,子进程发送信号给父进程,父进程更新统计信息,具体见 info persistence 下的 aof_* 相关统计。
  • 5.2、父进程把 AOF 重写缓冲区的数据写入到新的 AOF 文件。
  • 5.3、使用新 AOF 文件替换老文件,完成 AOF 重写。

重启恢复

AOF 与 RDB 的恢复流程:恢复优先级,AOF > RDB

image.png
流程说明:

  1. AOF 持久化开启且存在 AOF文件时,优先加载 AOF文件
  2. AOF 关闭或者 AOF 文件不存在时,加载 RDB 文件
  3. 加载 AOF/RDB 文件成功后,Redis 启动成功
  4. AOF/RDB 文件存在错误时,Redis 启动失败并打印错误信息

文件校验

加载损坏的 AOF 文件时会拒绝启动。

💡如 AOF 文件出现错误如何处理:

  • 优先针对错误文件进行备份
  • 使用 redis-check-aof --fix appendonly.aof 命令进行修复
  • 使用 diff 命令进行对比,进行人工修复

说明:AOF 文件可能存在结尾不完整的情况,比如机器突然掉电导致 AOF 尾部文件命令写入不全。可使用 aof-load-truncated 配置来兼容这种情况,默认开启。加载 AOF 时,当遇到此问题时会忽略并继续启动,并提示警告信息。

扩展阅读