vault backup: 2026-05-25 23:07:45
This commit is contained in:
+120
-7
@@ -67,12 +67,17 @@ appendfsync everysec # 每秒 fsync <- 推荐(最佳平衡)
|
||||
|
||||
| 策略 | fsync 时机 | 丢失风险 | 吞吐量 (ops/s) | 适用场景 |
|
||||
|------|-----------|---------|---------------|---------|
|
||||
| `always` | 每条写命令后 | **零丢失** | ~900 | 金融级要求(极少用) |
|
||||
| `always` | 每条写命令后 | **零丢失** | ~900(注) | 金融级要求(极少用) |
|
||||
| `everysec` | 每秒一次(后台线程) | 最多丢 ~1 秒 | ~74k | **生产标准配置** |
|
||||
| `no` | 从不主动 fsync,交给 OS | OS 决定,可能丢 30 秒+ | ~93k | 可接受数据丢失的场景 |
|
||||
|
||||
> [!note] 吞吐量数据来源
|
||||
> 以上数据来自 Redis 官方基准测试(`redis-benchmark`,HDD 磁盘)。实际生产环境中,`always` 在 SSD 上可达数万 ops/s,`everysec` 可达十万级。**具体性能以实际压测为准**,这里主要体现三者之间的相对差距。
|
||||
|
||||
> [!warning] everysec "最多丢 1 秒" 是有前提条件的
|
||||
> 这个说法成立的前提是 `fsync()` 能在 1 秒内完成。如果磁盘 IO 阻塞(比如同一块盘上跑着其他 IO 密集任务),一次 `fsync()` 可能卡住几秒甚至十几秒——此时未 fsync 的数据量会持续累积,丢失窗口也随之扩大。不过好在 Redis 的 `everysec` 模式下,`fsync()` 在**后台线程**执行,不会阻塞主线程处理新请求。
|
||||
> 这个说法成立的前提是 `fsync()` 能在 1 秒内完成。如果磁盘 IO 阻塞(比如同一块盘上跑着其他 IO 密集任务),一次 `fsync()` 可能卡住几秒甚至十几秒——此时未 fsync 的数据量会持续累积,丢失窗口也随之扩大。
|
||||
>
|
||||
> 补充一个实现细节:`everysec` 默认在**主线程**执行 `fsync()`,只有当 Redis 检测到上次 `fsync()` 耗时超过 2 秒时,才会自动降级到**后台线程**执行——这样避免长时间 fsync 阻塞主线程,但代价是主线程在降级期间的写入数据安全性进一步降低。
|
||||
|
||||
> [!tip] 为什么 always 这么慢?
|
||||
> `always` 是在**主线程**中同步调用 `fsync()`。一次 `fsync()` 耗时约 0.2~2ms(取决于磁盘),这意味着每条写命令都要等磁盘确认,吞吐量直接从万级跌到百级——这就是"数据安全 vs 性能"的经典权衡。
|
||||
@@ -122,6 +127,23 @@ auto-aof-rewrite-min-size 64mb # AOF 最小绝对大小(避免刚启动就
|
||||
>
|
||||
> 这本质上是一个「文件瘦身频率」的工程权衡题。
|
||||
|
||||
### 手动触发重写
|
||||
|
||||
除了自动触发,运维中经常需要手动重写——比如刚做了一次大批量数据导入,AOF 文件暴涨。
|
||||
|
||||
```bash
|
||||
# 手动触发 AOF 重写(后台异步执行,不阻塞主线程)
|
||||
BGREWRITEAOF
|
||||
|
||||
# 查看重写是否正在进行
|
||||
INFO persistence
|
||||
# aof_rewrite_in_progress: 1 ← 正在重写中
|
||||
# aof_rewrite_scheduled: 0 ← 是否有排队中的重写请求
|
||||
```
|
||||
|
||||
> [!warning] 重写互斥
|
||||
> 和 BGSAVE 一样,Redis 同一时刻**只允许一个子进程**做持久化。如果 BGSAVE 正在执行,`BGREWRITEAOF` 会排队等待(`aof_rewrite_scheduled: 1`),等 BGSAVE 完成后自动触发。
|
||||
|
||||
### 重写过程(类似 RDB fork)
|
||||
|
||||
重写并不是简单地"删减旧 AOF",而是**从内存中的真实数据出发,生成全新的精简 AOF 文件**。整个过程分三步:
|
||||
@@ -171,7 +193,7 @@ Redis 7.x 的多文件目录结构:
|
||||
```bash
|
||||
ls /var/lib/redis/appendonlydir/
|
||||
# total 256M
|
||||
# manifest.aof <- 文件清单(记录哪些文件组成完整 AOF)
|
||||
# appendonly.aof.manifest <- 文件清单(记录哪些文件组成完整 AOF)
|
||||
# base.rdb <- 最近一次重写生成的 RDB 全量快照
|
||||
# incr-0000000000000003.aof <- 增量 AOF(记录重写之后的新写操作)
|
||||
```
|
||||
@@ -179,10 +201,43 @@ ls /var/lib/redis/appendonlydir/
|
||||
> [!question] 为什么 Redis 7 要把单文件拆成目录结构?
|
||||
> 旧版单个 AOF 文件有一个痛点:重写时需要先生成临时文件,再 `rename` 替换。如果 AOF 有 50GB,rename 操作虽然原子但磁盘 IO 开销巨大。拆成多文件后,重写只需替换 `base.rdb` 和新增 `incr-*.aof`,IO 开销大幅降低,也更方便做增量备份。
|
||||
|
||||
### AOF 关键配置参数汇总
|
||||
|
||||
除了前面讲过的 `appendfsync`,还有几个配置项直接影响 AOF 的行为和稳定性:
|
||||
|
||||
```conf
|
||||
# ---- AOF 核心参数 ----
|
||||
appendonly yes # 开启 AOF
|
||||
appendfilename "appendonly.aof" # AOF 文件名
|
||||
appendfsync everysec # 刷新策略(always / everysec / no)
|
||||
|
||||
# ---- 重写相关 ----
|
||||
auto-aof-rewrite-percentage 100 # AOF 增长百分比触发重写
|
||||
auto-aof-rewrite-min-size 64mb # AOF 最小绝对大小才触发重写
|
||||
no-appendfsync-on-rewrite no # 重写期间是否暂停 fsync(见下方说明)
|
||||
|
||||
# ---- 容错相关 ----
|
||||
aof-load-truncated yes # 启动加载时是否允许截断末尾损坏的命令
|
||||
aof-use-rdb-preamble yes # 重写时是否使用 RDB 作为前缀(混合持久化开关)
|
||||
|
||||
# ---- Redis 7 新增 ----
|
||||
aof-timestamp-enable no # 是否在 AOF 中记录时间戳(用于 PITR)
|
||||
aof-ignore-errors no # fsync 失败时是否忽略错误继续运行
|
||||
```
|
||||
|
||||
| 参数 | 推荐值 | 要点 |
|
||||
|------|--------|------|
|
||||
| `no-appendfsync-on-rewrite` | `no` | 设为 `yes` 时,AOF 重写期间暂停 fsync 以减少磁盘争抢,但代价是**重写期间的数据安全性降级为 `no` 策略**——只在磁盘 IO 严重瓶颈时考虑 |
|
||||
| `aof-load-truncated` | `yes` | 设为 `yes` 时,Redis 启动发现 AOF 末尾不完整会自动丢弃末尾损坏命令并继续加载。设为 `no` 则直接报错退出——更安全但需要人工介入修复 |
|
||||
| `aof-timestamp-enable` | 按需 | Redis 7 的实验性功能,开启后 AOF 中会嵌入时间戳注释,配合 `redis-check-aof --timestamp` 可实现**按时间点恢复(PITR)**——比如"恢复到今天 14:30 的状态"。目前仍是实验特性,生产环境谨慎使用 |
|
||||
|
||||
> [!question] `no-appendfsync-on-rewrite` 什么时候该开?
|
||||
> 当你观察到 AOF 重写期间主线程出现明显延迟(`redis-cli --latency` 抖动),且 `INFO persistence` 显示 `aof_delayed_fsync` 持续增长时,可以考虑临时开启。这相当于用**重写期间的数据安全换性能**,是一把双刃剑。
|
||||
|
||||
## 混合持久化(Hybrid Persistence)
|
||||
|
||||
> [!tip] 混合持久化是 AOF 重写的"终极形态"
|
||||
> Redis 4.0 引入、Redis 7 默认开启。核心思路:AOF **重写**时先写 RDB 全量快照作为"底子",再追加增量 AOF 命令作为"补丁"——恢复时先加载 RDB(秒级),再回放少量增量 AOF(补齐数据)。
|
||||
> Redis 4.0 引入、Redis 5.0 开始默认开启(`aof-use-rdb-preamble yes`)。核心思路:AOF **重写**时先写 RDB 全量快照作为"底子",再追加增量 AOF 命令作为"补丁"——恢复时先加载 RDB(秒级),再回放少量增量 AOF(补齐数据)。
|
||||
>
|
||||
> **在 AOF 视角下**:重写后生成的文件不再全是文本命令,而是 `RDB 二进制基线 + AOF 增量日志` 的混合结构。好处是恢复速度快(接近纯 RDB)且数据安全性高(接近纯 AOF)。
|
||||
>
|
||||
@@ -217,7 +272,11 @@ ls /var/lib/redis/appendonlydir/
|
||||
redis-check-aof --fix appendonly.aof
|
||||
|
||||
# 场景三:Redis 7 多文件目录结构
|
||||
redis-check-aof --fix appendonlydir/appendonly.aof.1.incr.aof
|
||||
# 先查看 manifest 了解文件组成
|
||||
cat /var/lib/redis/appendonlydir/appendonly.aof.manifest
|
||||
# 修复具体的增量文件(找到损坏的那个)
|
||||
redis-check-aof --fix /var/lib/redis/appendonlydir/appendonly.aof.1.incr.aof
|
||||
# 注意:base.rdb 损坏需要用 redis-check-rdb 修复
|
||||
```
|
||||
|
||||
> [!tip] 实际修复流程
|
||||
@@ -233,14 +292,19 @@ flowchart TD
|
||||
FS["fsync() 调用"] --> OK{"返回成功?"}
|
||||
OK -->|"是"| CONT["继续正常服务"]
|
||||
OK -->|"否:EIO 磁盘错误"| LOG["记录日志<br/>I/O error writing to APPEND ONLY FILE"]
|
||||
LOG --> SD["执行 SHUTDOWN NOSAVE<br/>立即停止服务"]
|
||||
LOG --> CFG{"aof-ignore-errors<br/>配置?"}
|
||||
CFG -->|"no (默认)"| SD["执行 SHUTDOWN NOSAVE<br/>立即停止服务"]
|
||||
CFG -->|"yes (Redis 7+)"| IGN["忽略错误,继续服务<br/>但数据可能丢失"]
|
||||
|
||||
style SD fill:#fdd,stroke:#c00
|
||||
style IGN fill:#ffd,stroke:#c90
|
||||
style CONT fill:#dfd,stroke:#090
|
||||
```
|
||||
|
||||
> [!note] Redis 为什么会主动 shutdown?
|
||||
> 这是一种「fail-fast」设计哲学。AOF 的核心价值就是数据安全,当底层磁盘出现问题时,继续运行只会导致更多数据静默损坏。**宁可停机也不能让用户在"不确定"的数据上继续业务**——这和数据库遇到 WAL 写入失败时的处理逻辑是一样的。
|
||||
> 默认行为是一种「fail-fast」设计哲学。AOF 的核心价值就是数据安全,当底层磁盘出现问题时,继续运行只会导致更多数据静默损坏。**宁可停机也不能让用户在"不确定"的数据上继续业务**——这和数据库遇到 WAL 写入失败时的处理逻辑是一样的。
|
||||
>
|
||||
> Redis 7 引入了 `aof-ignore-errors` 配置项,设为 `yes` 时 Redis 会忽略 AOF 写入错误继续运行。但除非你完全理解后果(数据静默丢失),否则**不建议开启**。
|
||||
|
||||
> [!warning] AOF 不是万能的
|
||||
> - AOF 修复工具只能处理末尾截断,中间损坏只能截断丢失后面的数据
|
||||
@@ -267,6 +331,55 @@ flowchart TD
|
||||
> [!TIP] 生产环境黄金组合
|
||||
> `AOF everysec + 混合持久化 + 定时 rdb 冷备到对象存储`——几乎覆盖所有常见场景。
|
||||
|
||||
## 运维速查手册
|
||||
|
||||
### AOF 监控关键指标
|
||||
|
||||
```bash
|
||||
INFO persistence
|
||||
```
|
||||
|
||||
重点关注以下字段:
|
||||
|
||||
| 指标 | 含义 | 告警建议 |
|
||||
|------|------|----------|
|
||||
| `aof_enabled` | AOF 是否开启 | 应为 `1` |
|
||||
| `aof_rewrite_in_progress` | 是否正在重写 | 长时间为 `1` 需排查 |
|
||||
| `aof_rewrite_scheduled` | 是否有排队中的重写 | 长时间为 `1` 说明重写被阻塞 |
|
||||
| `aof_last_bgrewrite_status` | 上次重写是否成功 | ≠ `ok` 立即告警 |
|
||||
| `aof_last_write_status` | 上次 AOF 写入是否成功 | ≠ `ok` 说明磁盘可能有问题 |
|
||||
| `aof_current_size` | 当前 AOF 文件大小 | 持续膨胀未触发重写需检查配置 |
|
||||
| `aof_base_size` | 上次重写后 AOF 大小 | 结合 `aof_current_size` 计算增长比 |
|
||||
| `aof_delayed_fsync` | 被延迟的 fsync 次数 | 持续增长说明磁盘 IO 成为瓶颈 |
|
||||
|
||||
> [!question] 如何快速判断 AOF 是否健康?
|
||||
> 一个简单公式:`aof_current_size / aof_base_size`。如果比值远大于 `auto-aof-rewrite-percentage` 的配置值(默认 2.0),说明自动重写可能没有正常触发——检查 `auto-aof-rewrite-min-size` 和日志。
|
||||
|
||||
### 常用运维命令
|
||||
|
||||
```bash
|
||||
# ---- 手动触发 AOF 重写 ----
|
||||
BGREWRITEAOF
|
||||
|
||||
# ---- 查看 AOF 文件大小 ----
|
||||
ls -lh /var/lib/redis/appendonlydir/
|
||||
# Redis 7: 查看各组件文件
|
||||
ls -lh /var/lib/redis/appendonlydir/base.rdb
|
||||
ls -lh /var/lib/redis/appendonlydir/incr-*.aof
|
||||
|
||||
# ---- 查看 AOF 文件内容(前 10 条命令,RESP 格式) ----
|
||||
head -20 /var/lib/redis/appendonlydir/incr-0000000000000003.aof
|
||||
|
||||
# ---- 运行时动态调整 fsync 策略 ----
|
||||
redis-cli CONFIG SET appendfsync always # 临时切到最安全模式(如做批量导入前)
|
||||
redis-cli CONFIG SET appendfsync everysec # 恢复推荐配置
|
||||
```
|
||||
|
||||
> [!tip] 运维实战技巧
|
||||
> **大批量数据导入时**:临时切到 `appendfsync no` 或 `appendfsync everysec`,导入完成后再切回 `always`(如果需要)。避免每条写命令都 fsync 导致导入耗时翻数十倍。
|
||||
>
|
||||
> **磁盘空间告急时**:手动触发 `BGREWRITEAOF` 可以立即压缩 AOF 文件体积,通常能减少 50%-80% 的空间占用。
|
||||
|
||||
## 关联笔记
|
||||
|
||||
- [[hhs/Redis/04-RDB持久化]] — RDB 快照机制
|
||||
|
||||
Reference in New Issue
Block a user