Redis 中的数据持久化策略(AOF)

2,776 阅读6分钟

上一篇文章,我们讲的是 Redis 的一种基于内存快照的持久化存储策略 RDB,本质上他就是让 redis fork 出一个子进程遍历我们所有数据库中的字典,进行磁盘文件的写入。

但其实这种方式是有缺点的,先不说阻塞式 save 调用会阻塞整个 redis 服务,即便异步式 bgsave 也是基于时间间隔,每多少秒触发了多少次更新操作才会生成 RDB 文件,那么如果某次 RDB 生成之后,紧接着服务宕机,就至少丢失几秒甚至更多的数据,并且这些数据是无法挽回的。

而 AOF 是 redis 中的另一种数据持久化策略,它基于操作日志,也是一个很优秀的持久化策略,当然也有缺点。那么本篇就来讲讲这个 AOF 持久化策略。

一、什么是 AOF 持久化策略

AOF 即 append only file,当 redis 采用这这种数据持久化策略的时候,每当 redis 服务器收到一条更新命令时,操作结束之后会将这条命令添加到 aof 内存缓冲区,特定的时间下刷新缓冲区到磁盘文件中,也就是我们的 aof 文件。

默认的 redis 启动配置文件中,会有这么两条配置:

image

appendonly 指定 redis 是否启用 AOF 持久化策略,appendfilename 指明生成的 AOF 文件名称。

你也可以将 appendonly 选项指定为 yes,然后执行一条 set 命令,看看 redis 根目录下有没有生成一个 appendonly.aof 文件。

redis.conf 中还有 appendfsync 这么一条配置,它指明 AOF 文件的写入频率,即便 linux 中文件 IO 使用的高效的 epoll,但每收到一条更新命令就进行一次文件 IO,未免也太低效,况且也没必要。

appendfsync 的配置项有以下三种值可选:

  • always:每一次系统 serverCorn 函数调用就刷新一次缓存区
  • everysec:每秒执行一次磁盘写入,期间所有的命令都会存储在 aof 缓存区
  • no:不做控制,任由操作系统决定什么时候刷新缓冲区

redis 默认配置是 everysec,即每秒刷新一次缓存区。

二、AOF 重写

所以,理论上来说,随着 redis 服务器运行时间的持续,生成的 aof 文件只会越来越大,redis 提供 AOF 重写策略帮助优化和压缩 aof 文件。

比如:

set a "a"
set b "b"
set c "c"
del a
del b

正常情况下,aof 文件中会保存着五条命令的 log,然后数据恢复的时候依次执行即可。而当你启动 AOF 重写后,实际上我们的 aof 文件中只有 set c "c" 这一条命令的 log。

以上只是一个简单的示例,实际上 AOF 重写达到的效率比这优秀的多的多,往往能将几百条甚至几千条的命令日志,重写优化成个位数。带给我们最直观的好处就是,aof 文件体积变小,数据恢复速度变快。

一般来说,我们可以通过向 redis 服务器发送 bgrewriteaof 命令触发服务器对 aof 文件进行重写,如果当前有正在运行的重写子进程,则本次重写 会推迟执行,否则,直接触发一次重写。

除此之外,我们还可以在配置文件中配置 aof 文件达到多大,自动触发文件重写。

因为 aof 文件重写一样是 fork 子进程并由子进程处理的,主进程依然提供服务,所以 redis 还提供一块重写缓冲区,当发现有子进程正在进行 aof 文件重写,最新的请求命令除了会添加到 AOF 缓冲区,还会添加进 AOF 重写缓冲区,当子进程完成重写任务后,主进程阻塞式将重写缓冲区的命令日志添加进最新的 aof 文件中。

看几条配置

no-appendfsync-on-rewrite 配置了当 redis 服务器因为某些情况即将阻塞(例如 save)时是否需要将缓冲区中的 aof 命令写入到磁盘,配置 yes 则每次遇到阻塞操作时刷新缓存到磁盘,配置为 no 则无需关心服务器阻不阻塞,缓存命令在缓存区。

auto-aof-rewrite-percentage 配置了当 aof 文件相较于上一版本的 aof 文件大小的百分比达到多少时触发 AOF 重写。举个例子,auto-aof-rewrite-percentage 选项配置为 100,上一版本的 aof 文件大小为 100M,那么当我们的 aof 文件达到 200M 的时候,触发 AOF 重写。

auto-aof-rewite-min-size 配置了最小能容忍 aof 文件大小,超过这个大小必须进行 AOF 重写。

三、RDB 与 AOF

RDB 基于内存快照,有两种方式 save 和 bgsave,前者会阻塞 redis 服务,后者是异步 fork 子进程不影响主进程提供服务。大部分情况,我们会通过配置时间间隔触发 RDB 文件写入。RDB 文件中保存的是 redis 内存中所有的数据一份快照。

优点是:

  1. 相同的数据量下,rdb 文件要小于 aof 文件,且恢复速度要快于 aof
  2. rdb 文件中是整个数据的完整备份快照,数据存储紧凑即便不同版本的 redis,也能顺利恢复
  3. 整个 rdb 持久化,只需要 fork 一个子进程进行持久化即可,父进程依然可以提供服务,效率最大化

缺点是:

  1. 容易丢失数据,即便配置了事件时间触发备份,也至少丢失一秒数据
  2. 如果数据量太大,fork 子进程的时候会阻塞毫秒级别时间

AOF 是基于命令操作日志,每条更新命令都会被刷到缓存区,然后在特定的时间节点被写入 aof 磁盘文件。

优点是:

  1. 相较于 RDB,AOF 数据可靠性更强,最多丢失一秒数据
  2. 数据库容错率变好,一些误操作可以通过直接改 aof 文件进行回退

缺点是:

  1. AOF 文件通常较大且恢复效率比不上 RDB,不适合做数据冷备份

总的来说,AOF 策略会使数据稳定性更高,具有更完整的数据备份,RDB 恢复效率高适合做灾难恢复,建议生产环境上两者都开启。

ps:Redis 官方号称后续出一个新的持久化策略,整合 RDB 和 AOF 提供更高效率的数据持久化,期待中。