Redis 6.0/7.0 AOF重写方案

发布于2024-05-25
字数: 1627
Redis
AOF
MP-AOF

Redis 6.0/7.0 AOF重写方案

文章大纲

随着数据的不断写入,AOF文件会变得越来越大,为了解决问题,Redis使用了AOF重写机制来解决这个问题。在Redis7.0前后AOF重写方案有了比较大的变化,本文简单的讲解了两种方案的不同之处。

6.0的AOF重写

重写过程

流程说明:

1)开始执行AOF重写请求,如果当前正在执行bgsave保存RDB文件操作,则重写命令会延迟到basave完成之后再执行。

2)父进程执行fork创建子进程,开销等同于bgsave过程

3.1)主进程fork完操作之后,继续响应其他命令,所有修改命令依然写入AOF缓冲区并根据appendfsync策略同步到磁盘,保证原有AOF机制正确

3.2)由于fork操作运用写时复制(Copy-On-Write)技术,子进程只能共享fork操作时的内存数据,此时父进程仍然还在响应命令,redis使用"AOF重写缓冲区"保存这部分新数据,防止新AOF文件生成期间丢失这部分数据。可以通俗的理解生成新AOF文件的过程中产生的增量数据,保存在aof_rewrite_buf里面

4)子进程根据内存快照,按照命令合并规则写入到新的AOF文件,每次批量写入硬盘,默认为32MB写一次,由参数aof-rewrite-incremental-fsync控制,防止单次刷盘过多造成硬盘阻塞。

5.1)新AOF写入完成后,子进程发送信号给父进程,父进程更新统计信息

5.2)父进程把AOF重写缓冲区的数据写入到新的AOF文件

5.3)使用新的AOF文件替换老文件,完成AOF重写。

为了方便理解画了一个流程图。

6.0AOF流程图

从图中可以看到新请求不仅写入了AOF缓冲区还写入了AOF重写缓冲区,这是为什么呢?为什么不直接将重写期间保存在AOF缓冲区中的新命令在子进程写完临时文件后直接追加呢?因为AOF重写虽然是为了减少AOF文件的大小,但是**AOF重写是对数据库的重写而不是对AOF文件的重写。**Redis写磁盘的时机与MySQL不同,Redis是先写入内存,再写入磁盘,所以在创建子进程的那一刻,AOF缓冲区中可能还有数据未被写入AOF文件,所以我们需要创建一个新的缓冲区,来记录数据库的变化,这就是AOF重写缓冲区的意义所在。举个例子来说明:

在**T1时刻数据库有<span style="color: blue">"abcd"</span>这些数据,AOF缓冲区中的<span style="color: blue">"cd"</span>还未被写入AOF文件,这个时刻创建了子进程进行AOF重写,然后再T2时刻完成了重写。在T1 - T2期间,主进程往数据库写入了<span style="color: blue">"ef"</span>数据。假设AOF缓冲区的数据还未写入旧的AOF文件,那么在T2**时刻各个缓冲区和文件情况是这样的:旧的AOF文件有<span style="color: blue">"ab"</span>数据;AOF缓冲区有<span style="color: blue">"cdef"</span>数据,新的AOF文件有<span style="color: blue">"abcd"</span>数据;AOF重写缓冲区有<span style="color: blue">"ef"</span>数据。然后再将AOF重写缓冲区的数据追加到新的AOF文件,完成了AOF文件的重写。从这个过程中我们可以看出来AOF重写缓冲区的作用就是记录了重写期间数据库发生的变化。

6.0重写方案弊端

从上述的流程说明和流程图中我们可以看出6.0方案的弊端:

  1. 占用主进程CPU时间。重写期间需要额外向AOF重写缓冲区写数据;通过管道给子进程发送AOF重写缓冲区,还有子进程结束后将AOF重写缓冲区数据追加到临时文件中。

  2. 额外的内存开销。重写期间需要将fork之后的数据变化同时写入AOF缓冲区AOF重写缓冲区。这是Redis 6.2.7中开辟缓冲区的代码。

    c 复制代码
      /* This function is called by the child rewriting the AOF file to read
       * the difference accumulated from the parent into a buffer, that is
       * concatenated at the end of the rewrite. */
      ssize_t aofReadDiffFromParent(void) {
          char buf[65536]; /* Default pipe buffer size on most Linux systems. */
          ssize_t nread, total = 0;
      
          while ((nread =
                  read(server.aof_pipe_read_data_from_parent,buf,sizeof(buf))) > 0) {
              server.aof_child_diff = sdscatlen(server.aof_child_diff,buf,nread);
              total += nread;
          }
          return total;
      }
  3. 额外的磁盘开销,AOF缓冲区最终会写入旧的AOF文件,AOF重写缓冲区写入新的AOF文件,但是数据是一样的。

7.0的AOF重写

为了解决6.0AOF重写方案的弊端,Redis7.0引入了MP-AOF方案来解决。

MP-AOF方案概述

MP-AOF,全程Multi Part AOF,就是多部分AOF意思,通俗点讲就是由7.0的AOF文件部分由多个文件组成。MP-AOF文件有两个核心部分:

  1. BASE AOF文件,基础AOF文件,记录了基本的命令
  2. INCR AOF文件,记录了在重写过程新增的操作
7.0AOF流程图

从图中可以看到,重写期间,主进程只需要写入AOF缓冲区,最终AOF缓冲区的数据最终刷入新打开的AOF-incr.2文件,而子进程只需要根据fork时的数据库的数据重写并生成一个新的AOF-base.2文件,两个文件合起来就代表了当前时刻Redis的所有数据。在写完这两个文件之后通过manifest设置旧文件为history,这些history文件会被Redis异步删除掉。

总的来说在Redis7.0中,重写期间不需要AOF重写缓冲区,也不需要父子进程通过管道传输数据。这样一来,CPU、内存、磁盘的性能损耗会降低很多。


参考资料:

AOF重写过程详解-CSDN博客

一篇文章了解AOF持久化机制-腾讯云开发者社区-腾讯云 (tencent.com)

Redis persistence | Docs

redis7.0多AOF文件_多aof文件支持-CSDN博客