Redis学习09-AOF-混合持久化

发布于:2025-07-30 ⋅ 阅读:(27) ⋅ 点赞:(0)

AOF

AOF(Append Only File)持久化:以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。AOF解决了数据持久化的实时性问题

使用 AOF

  • 开启 AOF 功能需要设置配置:appendonly yes,默认不开启
  • AOF 文件名通过 appendfilename 配置(默认是 appendonly.aof)设置。
  • 保存目录同 RDB 持久化方式一致,通过 dir 配置指定
  • AOF 的工作流程操作:命令写入(append)、文件同步(sync)、文件重写(rewrite)、重启加载(load),如图所示。

redis.conf 配置示例

  1. 启用 AOF 持久化
    appendonly yes
  2. 设置 AOF 文件名(默认即为 appendonly.aof,可根据需要修改)
    appendfilename “appendonly.aof”
  3. 指定 AOF 文件保存目录(与 RDB 文件共享此配置)
    dir /var/lib/redis/data # 示实际使用时根据环境调整

将上述配置添加到或修改 Redis 配置文件(通常是 redis.conf)后,重启 Redis 服务即可生效

在这里插入图片描述

工作流程

  1. 所有的写入命令会追加到 aof_buf(缓冲区)中
  2. AOF 缓冲区根据对应的策略对硬盘做同步操作
  3. 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的
  4. 当 Redis 服务器启动时,可以加载 AOF 文件进行数据恢复

命令写入

AOF 命令写入的内容直接是文本协议格式。例如 set hello world 这条命令,在 AOF 缓冲区会追加如下文本:

1 *3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n

此处遵守 Redis 格式协议,Redis 选择文本协议的可能原因:文本协议具备较好的兼容性;实现简单;具备可读性

AOF 是每次把新的操作写入到原有文件的末尾,属于顺序写入

aof_buf 缓冲区

缓存区还是在内存中的,如果没有及时写入硬盘,数据还是会丢失的

AOF 过程中为什么需要 aof_buf 这个缓冲区?

Redis 使用单线程响应命令,如果每次写 AOF 文件都直接同步硬盘,性能从内存的读写变成 IO 读写,速度会下降。先写入缓冲区可以有效减少 IO 次数,同时,Redis 还可以提供多种缓冲区同步策略

文件同步

Redis 提供了多种 AOF 缓冲区同步文件策略,由参数 appendfsync 控制,不同值的含义如表所示。

AOF 缓冲区同步文件策略表

可配置值 说明
always 命令写入 aof_buf 后调用 fsync 同步,完成后返回
everysec 命令写入 aof_buf 后只执行 write 操作,不进行 fsync。每秒由同步线程进行 fsync
no 命令写入 aof_buf 后只执行 write 操作,由 OS 控制

系统调用 writefsync 说明:

  • write 操作会触发延迟写(delayed write)机制。Linux 在内核提供页缓冲区用来提供硬盘 IO 性能。write 操作在写入系统缓冲区后立即返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区页空间写满或达到特定时间周期。同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
  • fsync 针对单个文件操作,做强制硬盘同步,fsync 将阻塞直到数据写入到硬盘。
  • 配置为 always 时,每次写入都要同步 AOF 文件,性能很差,在一般的 SATA 硬盘上,只能支持大约几百 TPS 写入
  • 配置为 no 时,由于操作系统同步策略不可控,虽然提高了性能,但数据丢失风险大增
  • 配置为 everysec,是默认配置,也是推荐配置,兼顾了数据安全性和性能。理论上最多丢失 1 秒的数据。

重写机制

随着命令不断写入 AOF,文件会越来越大,为了解决这个问题,Redis 引入 AOF 重写机制压缩文件体积。AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新的 AOF 文件。

重写后的 AOF 为什么可以变小
  • 进程内已超时的数据不再写入文件。
  • 旧的 AOF 中的无效命令,例如 delhdelsrem 等重写后将会删除,只需要保留数据的最终版本。
  • 多条写操作合并为一条,例如 lpush list alpush list blpush list c 可以合并为 lpush list a b c

较小的 AOF 文件一方面降低了硬盘空间占用,一方面可以提升启动 Redis 时数据恢复的速度。

AOF 重写过程可以手动触发和自动触发:
  • 手动触发:调用 bgrewriteaof 命令。
  • 自动触发:根据 auto-aof-rewrite-min-sizeauto-aof-rewrite-percentage 参数确定自动触发时机。
    • auto-aof-rewrite-min-size:表示触发重写时 AOF 的最小文件大小默认为 64MB
    • auto-aof-rewrite-percentage:代表当前 AOF 占用大小相比较上次重写时增加的比例

如果在执行 bgrewriteaof 的时候,当前 redis 已经正在进行 aof 重写了

此时,不会再次执行 aof 重写,直接返回了。

如果在执行 bgrewriteaof 的时候,发现当前 redis 在生成 rdb 文件的快照

此时,aof 重写操作就会等待,等待 rdb 快照生成完毕之后,再进行执行 aof 重写

AOF 重写流程

在这里插入图片描述

  1. 执行 AOF 重写请求。
    如果当前进程正在执行 AOF 重写,请求不执行。如果当前进程正在执行 bgsave 操作,重写命令延迟到 bgsave 完成之后再执行。
  2. 父进程执行 fork 创建子进程。
  3. 重写
    a. 主进程 fork 之后,继续响应其他命令。所有修改操作写入 AOF 缓冲区并根据 appendfsync 策略同步到硬盘,保证旧 AOF 文件机制正确。
    b. 子进程只有 fork 之前的所有内存信息,父进程中需要将 fork 之后这段时间的修改操作写入 AOF 重写缓冲区中。
  4. 子进程根据内存快照,将命令合并到新的 AOF 文件中。
  5. 子进程完成重写
    a. 新文件写入后,子进程发送信号给父进程。
    b. 父进程把 AOF 重写缓冲区内临时保存的命令追加到新 AOF 文件中。
    c. 用新 AOF 文件替换老 AOF 文件。

父进程 fork 完毕之后,就已经让子进程写新的 aof 文件了,并且随着时间的推移,子进程很快就写完了新的文件,要让新的 aof 文件代替旧的。父进程此时还在继续写这个即将消亡的旧的 aof 文件是否还有意义?

考虑到极端情况。假设在重写过程中,重写了一半了,服务器挂了,子进程内存的数据就会丢失,新的 aof 文件内容还不完整。所以如果父进程不坚持写旧日 aof 文件,重启就没法保证数据的完整性了。

启动时数据恢复

当 Redis 启动时,会根据 RDB 和 AOF 文件的内容,进行数据恢复。
在这里插入图片描述

混合持久化

AOF 本来是按照文本的方式来写入文件的。但是文本的方式写文件,后续加载的成本比较高。Redis 就引入了混合持久化的方式,结合了 RDB 和 AOF 的特点。

按照 AOF 的方式,每一个请求/操作,都记录入文件。在触发 AOF 重写之后,就会把当前内存的状态按照 RDB 的二进制格式写入到新的 AOF 文件:

aof-use-rdb-preamble yes
这个选项为 yes 表示开启混合持久化
no(修改配置项之后要记得重新启动服务器)

后续再进行的操作,仍然是按照 AOF 文本的方式追加到文件后面

总结

  1. Redis 提供了两种持久化方案:RDB 和 AOF。
  2. RDB 视为内存的快照,产生的内容更为紧凑,占用空间较小,恢复时速度更快。但产生 RDB 的开销较大,不适合进行实时持久化,一般用于冷备和主从复制。
  3. AOF 视为对修改命令保存,在恢复时需要重放命令。并且有重写机制来定期压缩 AOF 文件。
  4. RDB 和 AOF 都使用 fork 创建子进程,利用 Linux 子进程拥有父进程内存快照的特点进行持久化,尽可能不影响主进程继续处理后续命令。

RDB 本身的设计理念,就是用来“定期备份”的。只要是定期备份,就难以保证数据的实时性
AOF 的理念则是实时备份


网站公告

今日签到

点亮在社区的每一天
去签到