前言
好哥哥们,这一篇的话是上一篇超详细解析 Redis 持久化之 RDB 的姐妹篇,关于
AOF
持久化。Redis 一共就提供了这两种持久化的方式,虽然平时很少会有对 Redis 的运维工作(能有几个好哥哥在生产环境有对 Redis 做过高可用和容灾备份的),但是这玩意在面试的时候经常被问到。也是掌握 Redis 比较重要的一个技能点了,还是很有必要说一说的呢。
概述
AOF
(append only file)持久化是以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。上篇关于RDB
持久化中有提到就是RDB
是不用用来做实时性或者秒级持久化的。那AOF
的主要作用是解决了数据持久化的实时性,打开AOF
后, 每当 Redis 执行一个改变数据集的命令时(比如 SET
), 这个命令就会被追加到 AOF
文件的末尾。这样的话, 当 Redis 重新启时, 程序就可以通过重新执行AOF
文件中的命令来达到重建数据集的目的。
AOF
持久化分成两个步骤:写入AOF
文件 和 异常数据恢复。
- 写入
AOF
文件原理图:
- 数据恢复原理图:
使用
AOF
持久化默认是不开启的,所以在使用之前需要需要设置配置(配置会贴在下面)。AOF
文件名通过appendfilename
配置设置,默认文件名是appendonly.aof
。保存路径同RDB
持久化方式一致,通过dir
配置指定。AOF 的工作流程操作:命令写入(append)
、文件同步(sync)
、文件重写(rewrite)
、重启加载(load)
。
1 开启AOF
这个配置和文件名、目录默认都在redis.conf
中
appendonly no
2 命令追加
AOF
追加的命令采用的是文本协议格式,这个格式实际上我们在不得不知道的 Redis 通信协议 中有讲过。好哥哥们可以看下这篇,下面就举个简单的栗子,还是以set hello world
为例,实际存储的就是如下的字符串。
*3\r\n$3\r\nSET\r\n$5\r\nhello\r\n$5\r\nworld\r\n
3 文件同步策略
可以通过配置文件配置 Redis 多久才将数据 fsync
( fsync
是针对单个文件操作,比如AOF
文件,做强制硬盘同步,fsync
将阻塞直到写入硬盘完成后返回) 到磁盘一次。AOF
提供了三种方式always
、no
、everysec
。
always
配置为 always 时,每次有新命令追加到 AOF 文件时就执行一次 fsync
。这种方式的话是三种策略中最慢的,也是最安全的。
no
从不执行fsync
,将数据交给操作系统来处理,由操作系统来决定什么时候同步数据。这种策略是最快的但是是最不安全的。
everysec
everysec
是默认的AOF
策略,表示每秒中执行 fsync
。这种策略是基于上面两种策略的一个中间策略,同时兼顾了性能和数据安全性。理论上来说故障时只会丢失 1 秒钟的数据。
三种策略对比
命令 | 优点 | 缺点 |
---|---|---|
always | 数据安全性最高 | I/O 开销大、性能最低、可能会阻塞服务器 |
no | 性能最好 | 不可控、风险大、存在数据不一致性的问题 |
everysec | 兼顾性能和安全 | 在某种情况下会丢失一秒钟的数据,也存在数据不一致性的问题 |
4 文件重写
这个实际上跟Mysql
中的查询语句优化是一样的,因为AOF
采用的是追加策略,这样会导致一个问题就是AOF
文件会越来越大。举个例子, 如果你对一个计数器调用了 100 次 INCR
, 那么仅仅是为了保存这个计数器的当前值, 那这个时候 AOF
文件就需要记录 100 条INCR
命令。然而在实际上, 只使用一条 SET
命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。
过程
当 Redis 执行AOF
文件重写时,会异步创建一个当前AOF
文件的体积优化版本。这个时候会出现两种情况,第一种就是执行重写时 Redis 没有接收到任何写入命令。第二种的话就是执行重写时 Redis 接收到了新的写操作命令(这个在注意点中会单独说)。不过即使 bgrewriteaof
执行失败,也不会有任何数据丢失,因为旧的 AOF
文件在 bgrewriteaof
成功之前不会被修改。
Redis 支持手动和自动触发对AOF
文件文件的重写
手动
直接在redis-cli
调用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
和 aof_base_size
可以在 info Persistence
统计信息中查看(这个条件可能会有很多版本)。
aof_current_size
大于auto-aof-rewrite-min-size
- (
aof_current_size
-aof_base_size
) /aof_base_size
>=auto-aof-rewrite-percentage
。
重写内容
- 进程内已经超时的数据不再写入文件。
- 去除旧的
AOF
文件含有的无效命令,如del key1
、hdel key2
、srem keys
、seta111
等。重写使用进程内数据直接生成,这样新的AOF
文件只保留最终数据的写入命令。 - 将多条写命令可以合并为一个。如:
lpush list a
、lpush list b
、lpush listc
可以转化为:lpush list a b c
。为了防止单条命令过大造成客户端缓冲区溢出,对于list
、set
、hash
、zset
等类型操作,以 64 个元素为界拆分为多条。
注意点
这里的话主要说一下,如果在执行AOF
重写时 Redis 接收到了新的写操作命令时出现的漏追加是怎么处理的。首先的话需要了解AOF
缓冲区AOF
重写缓冲区和两个概念。
AOF
缓冲区
AOF
缓冲区的主要的作用是当 Redis 开启了AOF
持久化后,当 Redis 接收到写操作命令后,就会将命令同步放到这个缓冲区内,然后根据配置的刷盘策略(上面的那三种)执行fsync
操作,然后清空。
AOF
重写缓冲区
AOF
重写缓冲区作用其实也是接收新的写操作命令,但是只会发生在AOF
重写期间。在这个期间内会一直接收新的写操作命令,知道重写完成后释放。
也就是说,当 Redis 执行AOF
文件重写时,会同时向AOF
缓冲区和重写缓冲区写入命令。往AOF
缓冲区写入命令的意义是旧的AOF
文件数据的一致性(根据配置也可能存在不一致),而AOF
重写缓冲区是保证优化过后新的AOF
文件的数据一致性,当子进程完成AOF
重写后,它会向父进程发送一个信号,父进程收到信号后会调用一个信号处理函数,该函数把AOF
重写缓冲区的命令追加到新AOF
文件中然后替换掉现有AOF
文件。
5 重启加载
AOF
和RDB
文件都可以用于服务器重启时的数据恢复。流程如下:
AOF
持久化开启且存在AOF
文件时,Redis 会优先加载AOF
文件。AOF
关闭或者AOF
文件不存在时,加载RDB
文件。- 加载
AOF
/RDB
文件成功后,Redis 启动成功。 AOF
/RDB
文件存在错误时,Redis 启动失败并打印错误信息。
优点
AOF
可以更好的保护数据不丢失,一般AOF
会以每隔 1 秒,通过后台的一个线程去执行一次fsync
操作,如果 Redis 挂掉了,最多丢失 1 秒的数据(非常适合用来做热备)。AOF
以append-only
的模式写入,所以没有任何的磁盘寻址的开销,写入性能非常的高。AOF
日志文件的命令通过非常可读的方式进行记录,这个非常适合做灾难性的误删除紧急恢复,如果某人不小心用flushall
命令清空了所有数据,只要这个时候还没有执行rewrite
,那么就可以将日志文件中的flushall
删除,进行恢复。
缺点
- 对于同一份数据备份文件,
AOF
比RDB
大。 AOF
开启后支持写的QPS
会比RDB
支持的写的QPS
低。因为AOF
一般会配置成每秒fsync
操作,每秒的fsync
操作还是很高的。- 数据恢复比较慢,不适合做冷备。
本期就到这啦,有不对的地方欢迎好哥哥们评论区留言,另外求关注、求点赞