Redis持久化AOF详解

基础面试题

什么是AOF

AOF(Append-Only File)用于将Redis服务器收到的写操作追加到日志文件,通过该机制可以保证服务器重启后依然可以依靠日志文件恢复数据。

它的工作过程大抵分为以下几步:

  1. 收到客户端的写入命令(例如SET、DEL等)之后,它会将命令写入AOF缓冲区。
  2. redis服务器会定期或者在特定条件下,将AOF缓冲区的数据以追加的方式写到日志文件末尾,这种写入的操作可以是同步的,也可以是异步的,具体看我们配置的刷盘机制。
  3. 若日志文件超过配置文件的大小(由配置参数 auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size 决定),则会触发AOF重写(AOF Rewrite),重写时会启动一个后台进程,分析日志中的指令并精简化写入新的AOF文件中。
  4. 新的AOF文件和旧的AOF文件进行原子替换,后续的写指令都会写到这个新的AOF文件中。

AOF写后记录日志有哪些优劣

有如下几个优势:

  1. 客户端操作的指令可能会出错,采用写后再日志的形式可以避免很多没必要的日志记录,节约磁盘空间
  2. 写日志需要进行磁盘IO,可能会产生阻塞,所以采用先写入再日志,可以避免写时阻塞。

当然劣势也很明显:

  1. 有可能在写操作之后,日志记录之前服务器出现宕机,可能会造成数据丢失
  2. 当主线程磁盘压力过大,导致写入磁盘慢,进而造成后续操作阻塞。

AOF核心配置参数有哪些

  1. appendonly :若将该参数设置为yes,则开启aof持久化机制,此时redis持久化机制就以aof为主,而非rdb

    设置为yes开启aof

    appendonly yes

如下示例所示,我们将该参数配置为yes后重启redis服务端,使用客户端完成如下操作

复制代码
# 设置三个key
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379>

此时我们查看aof文件,大小增加了

bash 复制代码
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# find / -name appendonly.aof
/usr/sbin/appendonly.aof
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# find / -name appendonly
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# find / -name appendonly.aof
/usr/sbin/appendonly.aof
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# ll appendonly.aof
-rw-r--r-- 1 root root 110 Aug 26 00:09 appendonly.aof

然后我们再次使用客户端写入文件

bash 复制代码
# 再次使用redis客户端写入指令
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# redis-cli
127.0.0.1:6379> set test vv
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123
OK
127.0.0.1:6379> set k4 v4
OK
127.0.0.1:6379>

可以看到大小又增加了,由此得出我们AOF配置生效了。

bash 复制代码
# 再次查看aof文件大小,变为139,说明aof配置生效
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# ll appendonly.aof
-rw-r--r-- 1 root root 139 Aug 26 00:10 appendonly.aof
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]#
  1. appendfilename ,该参数决定aof持久化文件的名字,这个就不多赘述了。 如下所示,这条配置就意味着aof文件名是appendonly
java 复制代码
appendfilename "appendonly.aof"
  1. dir :该参数决定aof文件持久化位置,默认为redis-server的位置。

    dir ./

  2. appendfsync : 在介绍appendfsync,我们必须介绍一下操作系统提供的两个函数

  1. write:write操作会触发操作系统延迟写机制,这种机制下数据一写到缓存区就直接返回,至于什么时候进行刷盘由操作系统决定,要么缓存空间满了刷,要么就是定时任务时间到了。
  2. fsync:该调用会强制将缓存写入磁盘中,所以使用这个函数进行文件写入时,可能存在阻塞问题。

了解了上述两个函数之后,我们再来聊聊这个参数值:

java 复制代码
1. `always`:该选项会使得命令一旦写入aof_buf后,就会调用操作系统的fsync将指令写到aof物理文件中,完成操作后线程返回
2. `everysec`:该选项会在命令写入`aof_buf`后调用操作系统的`wirte`,完成write后线程返回。`fsync`会由专门的线程每秒调用一次
3. `no`:该选项会在命令写入`aof_buf`后调用操作系统的`write`,完成`write`后线程返回,不调用`fsync`,同步操作由操作系统执行,最长周期为`30s`。

所以配置时,我们建议采用默认的写入策略everysec,他不会像always造成线程阻塞亦或者像no一样不可控。

复制代码
 appendfsync everysec
  1. no-appendfsync-on-rewrite:redis为了保证持久化aof文件时调用fsync时不会出现长时间的卡顿,增加了该参数,若设置为yes,在redis调用fsync期间出现的写入指令不会将其放到页缓存(page cache)中,仅仅做个接收,保证不阻塞。

    no-appendfsync-on-rewrite yes

  2. auto-aof-rewrite-percentage和auto-aof-rewrite-min-size(重点):这两个参数决定redis何时进行重写,如下所示,这两个参数分别为100和64mb,意味当本次aof文件超过64+64*100%就触发redis自动重写。

    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb

  3. aof-load-truncated:若设置为yes时在redis加载aof文件出错后会发送日志通知用户,反之则不做任何处理也不会启动redis,用户可以使用redis-check-aof指令完成数据修复。
    这个参数笔者会在后文演示。

    aof-load-truncated yes

  4. aof-rewrite-incremental-fsync:开启该参数后,子进程在进行aof重写时,每32m就会将数据写到的新的aof文件中,从而避免单刷造成的线程阻塞。

    aof-rewrite-incremental-fsync yes

  5. aof-use-rdb-preamble:redis 4.0之后支持同时开启rdb和aof,具体后文会详述

    rdb+aof两种机制结合使用

    aof-use-rdb-preamble yes

AOF断电后恢复的过程是什么

我们在之前的aof文件重命名,模拟断电后数据丢失,首先将aof文件备份,在重启redis,模拟断电后数据丢失

bash 复制代码
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# mv appendonly.aof appendonly.aof.bak


# 重启redis服务端,打开客户端查看数据都丢失了
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# redis-cli
127.0.0.1:6379> auth 123
OK
127.0.0.1:6379> keys *
(empty array)

然后将备份文件还原,重启redis。

复制代码
# 将aof文件还原,并重启redis
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# mv appendonly.aof.bak appendonly.aof
mv: overwrite 'appendonly.aof'? y
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# redis-server /root/redis/redis.conf

可以看到,数据已经回来了。

java 复制代码
# 再次使用redis查看,丢失的数据都回来了
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# redis-cli
127.0.0.1:6379> auth 123
OK
127.0.0.1:6379> keys *
1) "k4"
2) "k3"
3) "k2"
4) "k1"
127.0.0.1:6379>

进阶面试题

AOF重写机制如何压缩文件体积

如下图所示,可以看到重写时会查看进程内是否存在过期数据,如果数据过期则这个指令的操作也会被移除。

再一个我们之前可以存在对某个集合的元素添加操作,在重写时会将这些添加指令压缩成一条指令。

AOF重写时是否会阻塞线程

答案是会的,但阻塞仅仅发生在fork子进程那段时间,如下图所示,AOF重写时首先会fork一个子进程进行日志重写,在此期间新写入的数据都会被存到的AOF缓冲区中,直到子进程全部完成重写并原子覆盖aof日志文件后,才会将这些缓冲数据写到新的日志文件中。

需要补充的是,上面提到日志重写期间数据都会被写到AOF缓冲区中,在高并发场景下很可能导致内存被大量占用进而导致进程阻塞,所以Redis借由Linux管道技术使得在AOF日志重写期间的新增的数据照样可以写入到新文件中。

Redis重启后加载日志文件的顺序

执行顺序为:

  1. 先看看有没有AOF,若有则先加载AOF,然后执行步骤2。
  2. 查看是否有RDB文件,若有再加载RDB文件。

Redis恢复数据期间文件校验是怎么做

在日志写入期间要是服务器宕机了,那么这个日志文件可能就用不了了,而解决方案也很可能简单,redis给我提供一个命令进行fix。

例子如下,我们首先需要将一个日志文件损坏:

bash 复制代码
# 追加一个错误数据到aof文件末行并杀死redis 模拟服务器宕机
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# vim appendonly.aof


# 再次启动redis,操作数据时发现登录失败
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# redis-server /root/redis/redis.conf
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# redis-cli
Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected>

然后使用日志文件进行修复

bash 复制代码
#  使用 redis-check-aof --fix aof文件 修复文件
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# redis-check-aof --fix appendonly.aof
0x              8b: Expected prefix '*', got: 's'
AOF analyzed: size=151, ok_up_to=139, ok_up_to_line=34, diff=12
This will shrink the AOF from 151 bytes, with 12 bytes, to 139 bytes
# 这里选择y
Continue? [y/N]: y
Successfully truncated AOF

可以看到,经过fix修复后的日志文件部分数据已经恢复了

bash 复制代码
# 重启redis,使用客户端连接发现启动成功且数据都还在
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# redis-server /root/redis/redis.conf
[root@iZ8vb7bhe4b8nhhhpavhwpZ sbin]# redis-cli
127.0.0.1:6379> auth 123
OK
OK
127.0.0.1:6379> keys *
1) "k4"
2) "k3"
3) "k2"
4) "k1"

AOF有哪些优劣势

优势如下:

  1. 备份机制更稳健,丢失数据几率低。
  2. 日志可读,可以处理误操作。

而劣势也很明显:

  1. 比RDB更占磁盘空间,毕竟RDB存放的不是二进制文件。
  2. 每次AOF都进行fsync的话,性能开销大。
  3. 恢复和备份速度较慢。

redis混合持久化

Redis4.0实现了RDB和AOF混合方式,相比于单RDB或者单AOF更安全,执行效率更高,它的执行过程大抵如下:

  1. 初始状态下,写入的指令都会以RDB的形式写入RDB快照文件中。
  2. 当发生AOF重写时(bgrewriteaof ),redis会fork出一个子进程,此时会创建一个新的AOF文件。
  3. redis将全量rdb的数据写到新的aof文件中。
  4. 随后再将aof缓冲区的增量命令(aof_rewrite_buf_blocks)写到新的aof文件中。
  5. 完成上述操作后我们就会得到一个前半部分是RDB后半部分是AOF的aof日志文件。
  6. 最后将新的aof文件替换掉旧的rdb和aof文件。

参考文献

面试必问的 Redis:RDB、AOF、混合持久化:https://zhuanlan.zhihu.com/p/340082703

《Redis开发与运维》:https://book.douban.com/subject/26971561/

相关推荐
小猿姐15 分钟前
实测对比:哪款开源 Kubernetes MySQL Operator 最值得用?(2026 深度评测)
数据库·mysql·云原生
一灯架构2 小时前
90%的人答错!一文带你彻底搞懂ArrayList
java·后端
倔强的石头_2 小时前
从 “存得下” 到 “算得快”:工业物联网需要新一代时序数据平台
数据库
Y4090013 小时前
【多线程】线程安全(1)
java·开发语言·jvm
TDengine (老段)3 小时前
TDengine IDMP 可视化 —— 分享
大数据·数据库·人工智能·时序数据库·tdengine·涛思数据·时序数据
布局呆星3 小时前
SpringBoot 基础入门
java·spring boot·spring
风吹迎面入袖凉4 小时前
【Redis】Redisson的可重入锁原理
java·redis
GottdesKrieges4 小时前
OceanBase数据库备份配置
数据库·oceanbase
w6100104664 小时前
cka-2026-ConfigMap
java·linux·cka·configmap