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 的核心区别
| 维度 | RDB | AOF |
|---|---|---|
| 持久化方式 | 存储数据快照 | 存储写命令 |
| 文件大小 | 小(二进制) | 大(文本命令) |
| 恢复速度 | 快(直接加载) | 慢(重放命令) |
| 数据安全性 | 可能丢失快照间隔的数据 | 可配置(实时~秒级) |
| 性能影响 | 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 BGREWRITEAOFAOF 重写的原理(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 的启动加载顺序。
