8.6 KiB
tags, create time
| tags | create time | ||||
|---|---|---|---|---|---|
|
2026-05-15 18:13 |
AOF 持久化
概述
AOF(Append Only File)以追加写日志的方式记录每个写操作。相比 RDB 的快照式恢复,AOF 提供更高的数据安全性——你可以根据策略做到不丢数据、丢 1 秒、或丢 N 条。代价是文件更大、恢复更慢。
核心机制
命令追加过程
flowchart LR
Client["客户端写入"] --> Server["Redis 主线程<br/>执行命令 + 返回结果"]
Server --> Buffer["aof_buffer<br/>内核/用户态缓冲区"]
Buffer --> FSyncPolicy{"刷新策略?"}
FSyncPolicy -->|everysec| KernelBuf["操作系统页缓存"]
KernelBuf --> Disk["磁盘 aof 文件"]
FSyncPolicy -->|always| Disk
FSyncPolicy -->|no| OSPage["交给 OS 自己 flush"]
style Server fill:#f9d,stroke:#96c
style Disk fill:#dfd,stroke:#6c6
[!TIP] 一条命令是怎么落到磁盘的?
- 客户端发送命令 → 2. Redis 在主线程执行并返回结果 → 3. 同时将命令追加到
aof_buffer→ 4. 根据appendfsync策略最终刷入磁盘。整个过程对主线程几乎是异步的。
刷新策略配置
# appendfsync always # 每次写入都 fsync -> 最安全,性能差
appendfsync everysec # 每秒 fsync <- 推荐(最佳平衡)
# appendfsync no # 完全依赖 OS -> 性能最好,风险最高
| 策略 | 丢失风险 | 吞吐量 (ops/s) | 适用场景 |
|---|---|---|---|
always |
零丢失 | ~900 | 金融级要求(极少用) |
everysec |
最多丢 1 秒 | ~74k | 生产标准配置 |
no |
OS 决定,可能丢大量 | ~93k | 可接受全量丢失的场景 |
[!TIP] everysec 的性能真相
everysec不会阻塞主线程——它把 fsync 放到单独的后台线程。即使某个 fsync 花了 2s(磁盘卡顿),也只是这一秒的写入被推迟到下一周期,不会影响主线程。
AOF 重写(Rewrite)
AOF 文件会不断增长,需要定期重写来去除无效命令(如 key 被 DEL 后又 SET)。
重写触发条件
auto-aof-rewrite-percentage 100 # AOF 比上次 rewrite 后增大的百分比
auto-aof-rewrite-min-size 64mb # AOF 最小绝对大小(避免刚启动就 rewrite)
示例:当前 AOF = 128MB,上次 rewrite 后大小 = 64MB
- 增量 = 128 - 64 = 64MB
- 增量占比 = 64 / 64 = 100% -> 触发 rewrite
- 但如果当前 AOF < 64MB -> 不触发
[!QUESTION] 如果设置 auto-aof-rewrite-min-size 太大或太小会发生什么?
- 太大:AOF 膨胀了很久才触发重写,磁盘空间浪费严重
- 太小:频繁重写,增加不必要的 CPU 和 IO 开销
这本质上是一个「文件瘦身频率」的工程权衡题。
重写过程(类似 RDB fork)
sequenceDiagram
participant S as Redis Server
participant P as 主进程
child CG as Rewriter Child
participant C as 重写子进程
participant Log as appendonly.aof
participant NewLog as appendonly.aof_rewrite_tmp
end
S->>P: BGREWRITEAOF
P->>S: OK
P->>C: fork() 子进程
C->>NewLog: fork 时刻起遍历内存 -> 重写为精简命令
Note over P,C: Main process continues serving requests
loop 每个新写命令
P->>Log: 追加原始命令
P->>C: 同时写入 aof_rewrite_buf<br/>(重写期间的新操作)
end
C->>NewLog: 追加 aof_rewrite_buf 中的内容
C->>P: 完成
P->>Log: rename(原 -> 旧.bak, 新 -> 当前)
[!WARNING] 重写的内存开销 fork 后重写子进程也使用 COW 机制。如果你的 AOF 正在持续增长且写入量大,可能需要评估可用内存是否足够支撑 fork 的 COW 副本。在 100GB+ 的 Redis 实例上,一次 fork 可能导致额外的几 GB 内存占用。
AOF 开启方式
appendonly yes # 开启 AOF
appendfilename "appendonly.aof" # AOF 文件名
dir /var/lib/redis # AOF/RDB 文件存放目录
# Redis 7.x 目录结构(新版本使用多文件目录而非单文件)
ls /var/lib/redis/appendonlydir/
total 256M
drwxr-x--- 2 redis redis 4.0K May 15 18:00 .
drwxr-x--- 2 redis redis 4.0K May 15 18:00 ..
-rw-r----- 1 redis redis 16K May 15 18:00 manifest.aof
-rw-r----- 1 redis redis 128M May 15 18:00 base.rdb
-rw-r----- 1 redis redis 128M May 15 18:01 incr-0000000000000003.aof
思考:为什么 AOF 会持续增长?
想象以下操作序列:SET x 1 -> SET x 2 -> SET x 3 -> DEL x。如果不做处理,AOF 中会记录这 4 条命令,但 key x 已经不存在了——这些写入了磁盘却没有任何价值。这就是为什么需要重写。
混合持久化(Hybrid Persistence)
混合持久化是 Redis 4.0 引入、Redis 7 默认开启的特性——在 AOF 重写时,先用 RDB 快照全量备份当前内存状态,再用 AOF 追加增量变化。
# redis.conf
aof-use-rdb-preamble yes # 开启混合持久化(默认值)
flowchart LR
BG["BGREWRITEAOF"] --> Fork["fork 子进程"]
Fork --> RDBPart["RDB 全量部分<br/>二进制快照,写入极快"]
Fork --> AOFPart["AOF 增量部分<br/>仅含 fork 后的写操作"]
RDBPart --> Merge["合并为临时文件"]
AOFPart --> Merge
Merge --> Rename["原子 rename 替换"]
style RDBPart fill:#bbf,stroke:#66c
style AOFPart fill:#dfd,stroke:#090
style Merge fill:#ffd,stroke:#cc0
好处有两个:
- 恢复速度快:启动时先加载 RDB 全量快照(二进制格式比解析 AOF 命令快 10~100 倍),再回放少量 AOF 增量命令
- 文件体积小:相比纯 AOF 记录每条操作指令,混合模式下文件通常缩小 70%~90%
[!WARNING] 恢复流程的变化 混合模式下,Redis 启动时先读取
base.rdb还原全部数据,再逐条执行增量.aof段中的命令。如果只保留 AOF 段而删除base.rdb,恢复将退化为纯 AOF 模式,速度大幅下降。
AOF vs RDB 对比
| 维度 | AOF (everysec) |
RDB(典型配置) |
|---|---|---|
| 数据安全性 | ⭐⭐⭐ 最多丢 1s | ⭐ 可能丢数分钟 ~ 小时 |
| 恢复速度 | ⭐⭐ 较慢(需逐条 replay) | ⭐⭐⭐ 快(二进制直接加载) |
| CPU 开销 | ⭐⭐ 每秒一次 fsync | ⭐ 仅在 fork 时短暂增长 |
| 磁盘 IO | ⭐⭐ 持续写入日志 | ⭐ 仅在快照间隔触发 |
| 文件大小 | ⭐ 较大(逐条记录) | ⭐⭐⭐ 较小(二进制压缩) |
| 数据丢失窗口 | <= 1 秒 | = save 间隔时间 |
[!QUESTION] 什么时候该用 RDB 而不是 AOF? 如果你的业务场景是缓存而非数据库——数据可以重新构建或允许短暂丢失,那 RDB 就够了。只有当数据丢失会造成长期损失(如订单、余额)时,才需要上 AOF。
AOF 故障恢复
# 如果 AOF 损坏(断电等原因导致截断)
redis-server --repair appendonly.aof
# 或 Redis 7 下修复整个目录
redis-server --fix appendonlydir/aof-manifest
fsync 失败时的行为
fsync -> EIO(磁盘错误)
↓
ERROR "I/O error writing to APPEND ONLY FILE..."
↓
立即 SHUTDOWN NOSAVE(停止服务防止数据进一步损坏)
[!NOTE] Redis 为什么会主动 shutdown? 这是一种「fail-fast」设计哲学。AOF 的核心价值就是数据安全,当底层磁盘出现问题时,继续运行只会导致更多数据静默损坏。宁可停机也不能让用户在「不确定」的数据上继续业务。
[!WARNING] AOF 不是万能的
- AOF 修复工具只能恢复大部分,极端情况下会有部分命令丢失
- 定期做冷备份(RDB 迁移到 OSS/S3)仍然是必须的兜底手段
AOF vs RDB 选型决策树
flowchart TD
Start{"你的首要需求是?"} -->|"数据安全第一"| AOF["AOF everysec"]
Start -->|"恢复速度第一"| RDB["RDB 仅"]
Start -->|"两者兼顾"| HYBRID["混合持久化"]
HYBRID --> Check{"数据重要性极高?"}
Check -->|"是"| ALWAYS["AOF always<br/>+ 每天冷备"]
Check -->|"否"| EVERYSEC["AOF everysec<br/>+ 混合持久化"]
style HYBRID fill:#dfd,stroke:#090
style ALWAYS fill:#fdd,stroke:#900
style EVERYSEC fill:#dfd,stroke:#090
[!TIP] 生产环境黄金组合
AOF everysec + 混合持久化 + 定时 rdb 冷备到对象存储——几乎覆盖所有常见场景。
关联笔记
- hhs/Redis/04-RDB持久化 — RDB 快照机制
- hhs/Redis/06-主从与哨兵 — 哨兵监控中 AOF 状态检查
- hhs/Redis/09-运维调优 — AOF 文件膨胀治理