分享

图解 Redis AOF 持久化 | 创建与载入|重写

 小生凡一 2025-02-10 发布于福建

写在前面

除了RDB持久化外,Redis还提供了AOF(Append Only File)持久化功能。

  • RDB主要通过保存数据库中的键值对来记录数据库状态不同,也就是快照。
  • AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库的状态。
在这里插入图片描述
AOF保存过程

这篇文章我们就来讲讲AOF持久化的细节原理。

1. AOF持久化的实现

AOF 持久化功能的实验可以分为命令追加、文件写入、文件同步三个步骤

1.1 命令追加

当AOF持久化功能启动时,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的末尾

struct redisServer{
 // ...
 // AOF缓冲区
 sds aof_buf;
 // ...
};

举个例子:当客户端向服务器发送以下命令

redis>SET FanOne 1
OK

在服务器执行完这个SET命令后,会将以下协议内容追加到  aof_buf 缓冲区 中的末尾:

*3\r\n$3\r\nSET\r\n$6\r\nFanOne\r\n$1\r\n1\r\n
在这里插入图片描述
aof_buf追加过程

1.2 AOF文件的写入与同步

服务器会将命令先写入 aof_buf,再根据不同的appendfsync的值来写入AOF文件中。

在这里插入图片描述
aof同步过程
appendfsync
action
always
将aof_buf缓冲区中的所有内容实时写入到AOF文件中并完成磁盘同步
everysec
每隔1s将aof_buf缓冲区中所有的内容写到AOF文件中并完成磁盘同步
no
将 aof_buf 缓冲区中的所有内容写入AOF文件中,何时同步磁盘由操作系统决定
在这里插入图片描述
三种appendfsync的区别

可能有同学会对这里的fsync()存在疑惑,这里我们要注意一点:

一般来说,为了提高文件的写入效率,在现代操作系统中,当用户调用write函数,将一些数据写入到文件的时候,操作系统通过会将写入数据暂时保存在一个内存缓冲区中,等缓冲区的空间被填满或者超过了指定的时限后,才真正的将缓冲区中的数据写入到磁盘里面。

在这里插入图片描述这种做法虽然提高了效率,但也为写入数据带来了安全问题,因为如果机器发生停机,那么保存在内存缓冲区里面的写入数据就会丢失。为此,系统提供了fsync和fdatasync两个同步函数,可以强制让系统立刻将缓冲区中的数据写入磁盘中。

1.3 AOF 文件的载入与数据还原

因为AOF文件里面包含了重建数据库状态的所有写命令,所以服务器只要读入并重写执行一次AOF文件里面保存的写命令,就可以还原服务器关闭之前的数据库状态。Redis 读取AOF文件并还原数据库状态的步骤如下:

  1. 创建一个不带网络连接的伪客户端
  2. 从AOF文件中分析并读取一条写命令
  3. 使用伪客户端执行被读出来的写命令
  4. 一直重复2和3,直到所有的命令都读完成
在这里插入图片描述
AOF加载过程

2. AOF 重写

2.1 重写原理

因为AOF持久化是通过保存被执行的写命令来记录数据库状态的,所以随着服务器运行时间的流逝,AOF文件中的内容会越来越多,文件的体积也会越来越大如果不加以控制,体积过大的AOF文件会自身的机器实实例造成影响。

举个例子:

redis> SET FanOne 1 // FanOne:1
redis> SET FanOne 2 // FanOne:2
redis> SET FanOne 3 // FanOne:3
redis> SET FanOne 4 // FanOne:4

那么记录FanOne这个key和对应的value就要4条命令。

为了解决这个问题,Redis 提供了AOF文件重写(rewrite) 功能这样Redis服务器可以创建一个新的AOF文件来替代旧的AOF文件,新旧两个AOF文件所保存的数据库状态相同,但是新的AOF文件不会包含任何浪费空间的冗余命令,所以新的AOF文件的体积通常会比旧的AOF文件的体积要小很多。

在这里插入图片描述而重写的逻辑也非常简单:首先从数据库中读取键限制的值,然后用一条命令去记录键值对就可以了

在这里插入图片描述实际上,为了避免在执行命令时造成客户端输入缓冲区溢出,重写程序在处理列表、哈希表、集合、有序集合这四种可能会带有多个元素的键时,会先检查键所包含的元素数量,如果元素的数量超过了规定的阈值,那么重写程序就会用多个命令去记录键值,而不是单个命令。

2.2 后台重写

上面所述的aof_rewrite函数可以很好的完成创建一个新的AOF文件任务,但由于这个函数有大量的io操作,所以调用这个函数的线程将被长时间阻塞。

而redis是单个线程来处理命令请求,所以如果服务器直接调用aof_rewrite函数的话,那么在重写AOF文件的时候,服务器可能就无法处理客户端发来的命令请求。

所以Redis决定将AOF重写放在子进程中执行。

在这里插入图片描述
后台重写过程

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多