简介
AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。AOF的主要作用是解决了数据持久化的实时性。
类似 MySQL 的 Binlog 日志
写入与恢复原理:
使用 AOF
如何开启 AOF
开启 AOF 功能需要设置配置:CONFIG SET appendonly yes
,默认不开启。
AOF 文件名通过 appendfilename
配置设置,默认文件名是 appendonly.aof
。保存路径同 RDB 持久化方式一致,通过 dir
配置指定。
AOF 的工作流程
AOF的工作流程操作:命令写入 (append)、文件同步(sync)、文件重写(rewrite)、重启加载。
流程介绍:
- 所有写入命令会追加到 aof_buf 缓冲区
- 缓冲区针对不同策略,向硬盘做同步操作
- 随着 AOF 文件的增大,需要定期针对 AOF 文件进行重写,达到压缩的目的
- 当 Redis 重启服务时,可加载 AOF 文件进行恢复。
文件写入
AOF 命令写入的内容直接是文本协议格式。
例如 set bb 2
,将会在 AOF 文件追加如下文本:
*3
$3
set
$2
bb
$1
2
解释:
*3
为代表命令有三个关键词;$3/$2/$1
代表命令中对应的关键词字符长度
答疑:
- AOF 为什么直接采用文本协议格式?
- 文本协议具有很好的兼容性。
- 开启 AOF 后,所有写入命令都包含追加操作,直接采用协议格式,避免了二次处理开销。
- 文本协议具有可读性,方便直接修改和处理。
- AOF 为什么把命令追加到 aof_buf 中?
- Redis 为单线程响应命令,每次将 AOF 文件命令追加到硬盘,Redis 性能受限于硬盘负载。
- 先写入缓冲区 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 将阻塞直到写入硬盘完成后返回,保证了数据持久化。
不同策略的优缺点
策略 | 优点 | 缺点 |
---|---|---|
always |
不会丢失数据 | 硬盘读写 IO 性能消耗大 PS:大多数硬盘扛不住 |
everysec (默认值) |
每秒一次将命令同步到 AOF 文件,减少硬盘读写 IO 负载;能兼顾性能和数据安全。 | 当系统宕机会存在 1 秒的数据丢失情况 |
no |
提升性能 | 采用操作系统调度,同步周期不可控,数据安全性无法保证 |
重写机制
Redis 为减少命令的不断写入引起的 AOF 文件不断增大,引入了 AOF 的重写机制,来压缩文件体积。
AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新 AOF 文件的过程。
AOF 重写除了可降低文件占用空间以外,还可以加速 Redis 快速加载 AOF 文件。
体积变小
重写后的 AOF 文件变小的原因:
- 进程内已经超时的数据不再写入文件。
- 旧的 AOF 文件含有无效命令,如
del key1
、hdel key2
、srem keys
、set a111
、set a222
等。重写使用进程内数据直接生成,这样新的 AOF 文件只保留最终数据的写入命令。 - 多条写命令可以合并为一个,如:
lpush list a
、lpush list b
、lpush list c
可以转化为:lpush list a b c
。为了防止单条命令过大造成客户端缓冲区溢 出,对于list
、set
、hash
、zset
等类型操作,以 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-size
和auto-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_size
和aof_base_size
可以在info Persistence
统计信息中查看。
重写流程
流程说明:
- 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
流程说明:
- AOF 持久化开启且存在 AOF文件时,优先加载 AOF文件
- AOF 关闭或者 AOF 文件不存在时,加载 RDB 文件
- 加载 AOF/RDB 文件成功后,Redis 启动成功
- AOF/RDB 文件存在错误时,Redis 启动失败并打印错误信息
文件校验
加载损坏的 AOF 文件时会拒绝启动。
💡如 AOF 文件出现错误如何处理:
- 优先针对错误文件进行备份
- 使用
redis-check-aof --fix appendonly.aof
命令进行修复 - 使用
diff
命令进行对比,进行人工修复
说明:AOF 文件可能存在结尾不完整的情况,比如机器突然掉电导致 AOF 尾部文件命令写入不全。可使用
aof-load-truncated
配置来兼容这种情况,默认开启。加载 AOF 时,当遇到此问题时会忽略并继续启动,并提示警告信息。
扩展阅读
- linux 同步IO: sync、fsync与fdatasync:https://blog.csdn.net/zhouxinlin2009/article/details/89633464