Redis 持久化
问题
Redis 的 RDB 和 AOF 持久化有什么区别?如何选择?混合持久化是什么?
答案
一、RDB(Redis Database)
快照方式:将某一时刻的全量数据保存为二进制文件(dump.rdb)。
触发方式
# 手动触发
SAVE # 阻塞主线程(生产禁用)
BGSAVE # fork 子进程后台保存(推荐)
# 自动触发(redis.conf)
save 900 1 # 900 秒内至少 1 次写入
save 300 10 # 300 秒内至少 10 次写入
save 60 10000 # 60 秒内至少 10000 次写入
BGSAVE 流程
Copy-On-Write
fork 后子进程与父进程共享内存页。只有当父进程修改某个页时,才会复制该页。所以 BGSAVE 期间额外内存开销通常很小(取决于写入频率)。
RDB 优缺点
| 优点 | 缺点 |
|---|---|
| 文件紧凑,恢复速度快 | 两次快照之间的数据会丢失 |
| 适合备份和灾难恢复 | fork 大内存时可能阻塞 |
| 对性能影响小 | 不适合高数据安全要求 |
二、AOF(Append Only File)
追加日志方式:将每条写命令追加到文件末尾。
写入流程
写命令 → AOF 缓冲区 → write() 到 page cache → fsync 到磁盘
同步策略
配置 appendfsync | 行为 | 数据安全 | 性能 |
|---|---|---|---|
| always | 每条命令都 fsync | 最多丢 1 条 | 最低 |
| everysec | 每秒 fsync | 最多丢 1 秒 | 推荐 |
| no | 不主动 fsync | 取决于 OS | 最高 |
AOF 重写
AOF 文件会持续增长,需要定期 重写压缩:
# 手动触发
BGREWRITEAOF
# 自动触发
auto-aof-rewrite-percentage 100 # AOF 文件比上次重写后增长 100%
auto-aof-rewrite-min-size 64mb # AOF 文件至少 64MB 才触发
重写原理:不是对旧 AOF 文件操作,而是根据当前内存数据 生成最少的命令集。例如:
# 重写前(100 条 INCR)
INCR counter # ×100
# 重写后(1 条 SET)
SET counter 100
三、RDB vs AOF 对比
| 对比项 | RDB | AOF |
|---|---|---|
| 数据安全 | 可能丢失两次快照间的数据 | 最多丢 1 秒 |
| 文件大小 | 小(二进制压缩) | 大(文本命令) |
| 恢复速度 | 快 | 慢(重放命令) |
| 性能影响 | fork 时有瞬间开销 | 持续写入有 IO 开销 |
| 适用场景 | 备份、灾难恢复 | 数据安全要求高 |
四、混合持久化(4.0+,推荐)
aof-use-rdb-preamble yes # 默认开启(Redis 5.0+)
AOF 重写时:先写 RDB 格式的全量数据,再追加重写期间的 AOF 增量命令:
┌──────────────────────┬────────────────┐
│ RDB 格式全量数据 │ AOF 增量命令 │
└──────────────────────┴────────────────┘
恢复时:先加载 RDB 部分(快),再重放 AOF 部分(少量命令)。
生产推荐
- 开启混合持久化(默认)
appendfsync设为 everysec- 定期将 RDB 文件备份到异地
常见面试问题
Q1: RDB 和 AOF 可以同时开启吗?
答案:
可以。同时开启时,Redis 重启 优先使用 AOF 恢复数据(因为 AOF 的数据更完整)。推荐同时开启:AOF 保证数据安全,RDB 用于备份。
Q2: fork 子进程会阻塞吗?
答案:
fork 本身是需要复制页表的,如果 Redis 实例内存很大(如 20GB+),fork 可能需要几十到几百毫秒,这段时间主进程会阻塞。
优化方式:
- 控制 Redis 实例内存不要太大(建议单实例不超过 10GB)
- 使用
info stats监控latest_fork_usec指标 - 关闭 Transparent Huge Pages(THP)
Q3: AOF 文件损坏了怎么办?
答案:
# 检查并修复 AOF 文件
redis-check-aof --fix appendonly.aof
该工具会截断 AOF 文件中最后不完整的命令,可能会丢失少量数据。所以建议同时保留 RDB 备份。