Mr Dk.'s BlogMr Dk.'s Blog
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • 🦆 About Me
  • ⛏️ Technology Stack
  • 🔗 Links
  • 🗒️ About Blog
  • Algorithm
  • C++
  • Compiler
  • Cryptography
  • DevOps
  • Docker
  • Git
  • Java
  • Linux
  • MS Office
  • MySQL
  • Network
  • Operating System
  • Performance
  • PostgreSQL
  • Productivity
  • Solidity
  • Vue.js
  • Web
  • Wireless
  • 🐧 How Linux Works (notes)
  • 🐧 Linux Kernel Comments (notes)
  • 🐧 Linux Kernel Development (notes)
  • 🐤 μc/OS-II Source Code (notes)
  • ☕ Understanding the JVM (notes)
  • ⛸️ Redis Implementation (notes)
  • 🗜️ Understanding Nginx (notes)
  • ⚙️ Netty in Action (notes)
  • ☁️ Spring Microservices (notes)
  • ⚒️ The Annotated STL Sources (notes)
  • ☕ Java Development Kit 8
GitHub
  • ⛸️ Redis Implementation
    • Part 1 - 数据结构与对象

      • Chapter 2 - 简单动态字符串
      • Chapter 3 - 链表
      • Chapter 4 - 字典
      • Chapter 5 - 跳跃表
      • Chapter 6 - 整数集合
      • Chapter 7 - 压缩列表
      • Chapter 8 - 对象
    • Part 2 - 单机数据库的实现

      • Chapter 9 - 数据库
      • Chapter 10 - RDB 持久化
      • Chapter 11 - AOF 持久化
      • Chapter 12 - 事件
      • Chapter 13 - 客户端
      • Chapter 14 - 服务器
    • Part 3 - 多机数据库的实现

      • Chapter 15 - 复制
      • Chapter 16 - Sentinel
      • Chapter 17 - 集群
    • Part 4 - 独立功能的实现

      • Chapter 18 - 发布与订阅
      • Chapter 19 - 事务
      • Chapter 21 - 排序
      • Chapter 22 - 二进制位数组
      • Chapter 23 - 慢查询日志

Chapter 11 - AOF 持久化

Created by : Mr Dk.

2020 / 06 / 05 15:50

Nanjing, Jiangsu, China


AOF (Append Only File) 通过保存 Redis 服务器所执行的写命令来记录数据库状态。在服务器启动时,可以直接载入 AOF 文件中保存的命令,还原服务器关闭之前的数据库状态。


Implementation of AOF

AOF 持久化功能由三个步骤实现:

  • 命令追加
  • 文件写入
  • 文件同步

命令追加

服务器执行完一个写命令之后,会按照 AOF 的格式把被执行的命令 append 到服务器中的 aof_buf 缓冲区中。这个缓冲区被维护在服务器状态中:

struct redisServer {
    // ...

    sds aof_buf;

    // ...
}

文件写入与同步

Redis 的服务器进程是一个 事件循环,在每一轮循环中,主要干了三件事情:

  • 处理文件事件 (接收客户端命令并发送回复)
  • 处理时间事件 (周期性定时事件)
  • 刷新 AOF

刷新 AOF 的行为有三种 (默认使用 everysec,可以通过配置修改):

  • always - 将 AOF 缓冲区中的所有内容 写入 并 同步 到 AOF 文件
  • everysec - 将 AOF 缓冲区中的所有内容 写入 AOF 文件,如果距离上次同步超过 1s,则 同步 AOF 文件
  • no - 将 AOF 缓冲区中的所有内容 写入 AOF 文件,但 不同步

文件的写入和同步有何区别?

为加快对文件的读写,OS 通常会在内存中为文件开辟高速缓冲。当缓冲被填满,或超过指定时限后,才将缓冲区的内容真正同步到磁盘上。效率的提升会带来安全问题 (如果计算机发生停机,内存中还未同步到磁盘上的数据将丢失)。OS 提供 fsync 和 fdatasync 两个同步的函数,可以强制立刻将缓冲区中的数据写入到硬盘里。

以上三种 AOF 行为:

  • always - 性能最差,但同时也是最安全的
  • everysec - 是性能与安全性的折衷,最多只会丢失一秒钟内的命令
  • no - 由 OS 控制何时同步,所以性能最好;但该模式的 单次同步时长 最长;若发生故障,将丢失从上一次同步开始的所有数据

AOF 文件载入与还原

Redis 会创建一个不带网络连接的伪客户端,然后依次执行 AOF 文件中的命令,就能够恢复数据库的状态。


AOF Rewrite

由于 AOF 是 append only 的,那么文件中的内容必然会越来越多。Redis 能够创建一个新的 AOF 文件,并在其中删除冗余命令,同时保持数据库状态不变,然后原子地替换旧的 AOF 文件。

从原理上来说,读取某个时刻的数据库状态,就能够替代之前一切对于数据库进行写操作的命令。在重写之后,AOF 文件中只包含还原当前数据库状态所必须的命令。因此,只需要使用 SADD 或 RPUSH 记录数据库的状态,就能压缩 AOF 文件。

在重写期间必然会产生大量的写入操作。为了不阻塞服务器主进程,AOF 重写应当由子进程在后台完成:

  • 子进程 AOF 重写期间,服务器主进程可以继续接收请求命令
  • 子进程带有服务器主进程的 数据副本,因此,在子进程写入数据期间,数据不会受到主进程的影响

仅剩的一个问题是,子进程在进行 AOF 重写期间,服务器主进程执行的命令没有被记录。为了解决这个问题,Redis 设置了一个 AOF 重写缓冲区,这个缓冲区只有在 AOF 重写期间才会被启用。在 AOF 重写期间,服务器主进程每执行一条命令,同时也需要在 AOF 重写缓冲区中记录。

当子进程完成 AOF 重写后,会向主进程发送一个信号。在主进程的信号处理函数中:

  1. 将 AOF 重写缓冲区中的内容加入到新的 AOF 文件中
  2. 原子地将新的 AOF 文件覆盖现有的 AOF 文件

整个 AOF 重写的过程中,只有在执行这个信号处理函数时,服务器主进程会被阻塞。使 AOF 重写对服务器性能造成的影响降到了最低。

怎么和 JVM 的 GC 过程有些类似呢...

Edit this page on GitHub
Prev
Chapter 10 - RDB 持久化
Next
Chapter 12 - 事件