Skip to content

AOF:追加文件持久化原理与模式

RDB 是快照,咔嚓一下拍一张全量照片。

但如果你的业务要求每秒钟最多丢失 1 秒数据,快照就不够用了。

这时候,你需要 AOF(Append Only File)

AOF 是什么?

AOF 的核心理念是:记录所有写操作命令,而不是存储数据本身

┌─────────────────────────────────────────────────────────────────┐
│                        Redis 内存数据                            │
│                                                                 │
│   SET name "张三"    INCR views    HSET user:1 age 25          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

                              │ 写操作

┌─────────────────────────────────────────────────────────────────┐
│                      appendonly.aof 文件                        │
│                                                                 │
│   *3\r\n$3\r\nSET\r\n$4\r\nname\r\n...                        │
│   *2\r\n$4\r\nINCR\r\n$5\r\nviews\r\n                         │
│   *4\r\n$4\r\nHSET\r\n...                                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

恢复时,Redis 重新执行这些命令,就能还原数据状态。

AOF vs RDB 的核心区别

维度RDBAOF
持久化方式存储数据快照存储写命令
文件大小小(二进制)大(文本命令)
恢复速度快(直接加载)慢(重放命令)
数据安全性可能丢失快照间隔的数据可配置(实时~秒级)
性能影响fork 子进程,不阻塞持续写入,有 I/O 开销

AOF 的三种写入策略

这是 AOF 的核心,也是面试常考点。

1. always:每条命令都刷盘

bash
# redis.conf
appendfsync always
客户端 → 主线程执行命令 → 写入 AOF 缓冲区 → fsync() 刷盘 → 返回

优点:数据完全不丢失 缺点:每个命令都要刷盘,性能最差

2. everysec:每秒刷盘一次(默认)

bash
# redis.conf
appendfsync everysec
客户端 → 主线程执行命令 → 写入 AOF 缓冲区

                                   │ 每秒一次

                              fsync() 刷盘

优点:最多丢失 1 秒数据,性能适中 缺点:极端情况下可能丢失 1 秒数据

3. no:操作系统决定

bash
# redis.conf
appendfsync no
客户端 → 主线程执行命令 → 写入 AOF 缓冲区 → 返回

                                          │ 操作系统决定何时刷盘

                                     fsync() 刷盘

优点:性能最好 缺点:取决于操作系统,可能丢失较多数据

AOF 的工作流程

完整的 AOF 流程

┌─────────────────────────────────────────────────────────────────┐
│                         客户端请求                               │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                       主线程处理请求                             │
│                                                                 │
│   1. 命令解析                                                   │
│   2. 数据操作(写入内存)                                        │
│   3. 命令追加到 AOF 缓冲区                                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│                    AOF 缓冲区(内存)                            │
│                                                                 │
│   [SET name "张三"][INCR views][HSET user:1 age 25]            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

                              │ appendfsync 策略

┌─────────────────────────────────────────────────────────────────┐
│                    操作系统 Page Cache                           │
└─────────────────────────────────────────────────────────────────┘

                              │ 操作系统决定

┌─────────────────────────────────────────────────────────────────┐
│                       磁盘文件                                   │
│                       appendonly.aof                            │
└─────────────────────────────────────────────────────────────────┘

AOF 重写机制

随着时间推移,AOF 文件会越来越大,因为记录了大量冗余命令:

# 原始 AOF 文件
SET name "张三"
SET age "25"
SET city "北京"
SET age "26"      # age 被覆盖了,但这条命令还在
SET age "27"      # 又被覆盖了
SET age "28"
...(几百条覆盖 age 的命令)

AOF 重写(Rewrite)会压缩 AOF 文件

# 重写后的 AOF 文件
SET name "张三" age "28" city "北京"

AOF 重写的触发

bash
# 自动重写(根据文件大小增长触发)
auto-aof-rewrite-percentage 100  # 文件大小达到上次重写后的 100%
auto-aof-rewrite-min-size 64mb   # 文件达到 64MB 才触发重写

手动触发:

bash
redis-cli BGREWRITEAOF

AOF 重写的原理(COW + 临时文件)

┌─────────────────────────────────────────────────────────────────┐
│                         主进程                                   │
│                                                                 │
│   数据区域 ──→ [Key1][Key2][Key3]...[KeyN]                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

                              │ fork()

┌─────────────────────────────────────────────────────────────────┐
│                       子进程(AOF 重写)                         │
│                                                                 │
│   读取内存数据,生成最小命令序列                                  │
│   写入临时文件:temp-aof-rewrite-buffer.aof                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

                              │ 重写期间,主进程继续接收写命令

┌─────────────────────────────────────────────────────────────────┐
│                    AOF 重写缓冲区(内存)                        │
│                                                                 │
│   [新写入的命令...]                                             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

                              │ 重写完成

┌─────────────────────────────────────────────────────────────────┐
│                      合并 + 替换                                │
│                                                                 │
│   临时文件 + AOF 重写缓冲区 → appendonly.aof                     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

AOF 配置详解

bash
# redis.conf

# 开启 AOF
appendonly yes

# AOF 文件名
appendfilename "appendonly.aof"

# AOF 存放目录(与 RDB 共享 dir 配置)
dir /var/lib/redis

# 写入策略
appendfsync everysec

# AOF 重写配置
auto-aof-rewrite-percentage 100   # 增长 100% 时触发重写
auto-aof-rewrite-min-size 64mb   # 最小 64MB 才触发

# 是否加载不完整的 AOF 文件
aof-load-truncated yes           # yes: 跳过错误继续加载;no: 启动失败

# 是否启用 RDB+AOF 混合持久化
aof-use-rdb-preamble yes         # Redis 4.0+,重写时用 RDB 格式加速

AOF 的优缺点

优点

优点说明
数据安全性高可配置为每秒刷盘,甚至每条命令刷盘
日志格式AOF 文件是文本,易于人工查看和修复
自动重写自动压缩冗余命令

缺点

缺点说明
文件较大同样的数据,AOF 比 RDB 大
恢复慢需要重放所有命令
可能阻塞everysec 模式每秒同步时可能阻塞

生产环境最佳实践

1. 选择合适的刷盘策略

java
/**
 * 刷盘策略选择指南:
 * 
 * always:金融交易、支付等绝对不能丢数据的场景
 * everysec:大多数业务场景(默认推荐)
 * no:追求极致性能,允许少量数据丢失
 */

2. 监控 AOF 文件大小

bash
# 查看 AOF 文件信息
redis-cli INFO persistence

# AOF 文件
aof_current_size: 1073741824    # 当前大小(字节)
aof_base_size: 536870912       # 上次重写时的大小
aof_pending_rewrite: 0          # 是否有重写任务待执行

# AOF 写入状态
aof_last_write_status: ok       # 上次写入状态
aof_last_write_buf_sizes: ...   # 缓冲区大小

3. 开启混合持久化

Redis 4.0 引入了 RDB+AOF 混合持久化

bash
aof-use-rdb-preamble yes

混合模式下,AOF 重写时:

┌─────────────────────────────────────────────────────────────────┐
│                       混合 AOF 文件                              │
│                                                                 │
│   [RDB 格式头部] │ [AOF 格式增量命令]                           │
│   (数据快照)      │ (重写期间的增量)                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

优势

  • 恢复速度更快(先用 RDB 快速加载,再用 AOF 增量重放)
  • AOF 文件更小

4. 防止 AOF 文件损坏

bash
# 开启 AOF 校验和
aof-use-rdb-preamble yes

# 加载时检查
aof-load-truncated yes  # yes:跳过错误继续加载(可能有少量数据丢失)
                        # no:启动失败,需要修复

Java 客户端使用

java
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;

public class RedisAOFDemo {
    public static void main(String[] args) {
        try (Jedis jedis = new Jedis("localhost", 6379)) {
            
            // 开启 AOF(需要 Redis 配置支持)
            // jedis.configSet("appendonly", "yes");
            
            // 手动触发 AOF 重写
            jedis.bgrewriteaof();
            
            // 获取 AOF 相关信息
            String aofEnabled = jedis.configGet("appendonly").get(1);
            String appendfsync = jedis.configGet("appendfsync").get(1);
            System.out.println("AOF 开启状态: " + aofEnabled);
            System.out.println("刷盘策略: " + appendfsync);
        }
    }
}

AOF 损坏的修复

如果 AOF 文件损坏,可以尝试修复:

bash
# 1. 备份损坏的文件
cp appendonly.aof appendonly.aof.bak

# 2. 使用 Redis 自带工具修复
redis-check-aof --fix appendonly.aof

# 3. 检查修复结果
redis-check-aof appendonly.aof

总结

AOF 是 Redis 持久化的另一种选择:

  • 原理:记录写命令到文件
  • 策略:always/everysec/no
  • 重写:压缩冗余命令
  • 混合持久化:RDB + AOF,取长补短

留给你的问题

如果 AOF 文件损坏了,但 RDB 文件完好,Redis 会怎么恢复数据?

提示:考虑 Redis 的启动加载顺序。

基于 VitePress 构建