Redis 持久化 RDB vs AOF 怎么选?——数据安全 vs 性能

引言

"凌晨 Redis 主节点物理机故障,哨兵把流量切到了从节点,业务几分钟就恢复了——但早上对账发现,故障前 4 分钟内的 1200 笔已支付订单状态全部丢失。" 支付服务把订单状态临时存在 Redis 里做状态机流转,默认配置下只有 RDB、save 60 10000(60 秒内 1 万次写入才触发快照),深夜低峰期写入稀疏,最后一次成功快照停在故障前 4 分钟。更麻烦的是这些状态已经驱动了后续业务,丢了就补不回来。

另一个极端同样常见:有人被这次事故吓到,把所有实例都开成 appendfsync always,结果每条写命令都强制刷盘,单实例写入 QPS 从 5 万掉到 2000,应用线程大量阻塞在 write() 上——为了不丢 1 秒钟的数据,付出了 25 倍吞吐的代价,这显然也不是正确答案。

Redis 持久化的选型之所以争议不断,是因为它从来不是"RDB 还是 AOF"的二选一,而是一连串工程权衡:能接受丢多久的数据、文件多大、恢复多快、fork 子进程对大实例的卡顿有多大、重写期间的双写内存撑不撑得住。这篇文章从持久化的本质讲起,把 RDB 快照、AOF 追加日志、AOF 重写、混合持久化四个机制拆开讲透,最后给出按业务场景对号入座的选型表和可直接抄的生产配置(主线基于 Redis 6.2/7.x,7.0 的 Multi-Part AOF 新结构会单独说明)。先给结论:今天 95% 的生产场景标准答案是"混合持久化 + appendfsync everysec"——RDB 保证恢复快,AOF 保证最多丢 1 秒,而不是任何一个极端。


一、全景:Redis 为什么需要持久化,持久化到底在保存什么

1.1 内存数据库的命门

Redis 的所有数据在内存里,进程退出、机器宕机、电源故障,内存内容全部消失。持久化就是把内存中的数据状态以某种格式写到磁盘,重启时再加载回来。需要先建立一个认知:Redis 持久化保存的是"数据状态",但两种机制记录状态的方式完全不同:

RDB(Redis DataBase):拍快照
  某一时刻内存里有什么 → 直接序列化成紧凑二进制 → dump.rdb
  思路:记录"结果"

AOF(Append Only File):写日志
  每执行一条写命令 → 把命令文本追加到日志 → appendonly.aof
  重启时把日志里的命令从头回放一遍,重建数据
  思路:记录"过程"

这一个差异决定了两者的全部特性:快照天然紧凑、恢复快,但两次快照之间的数据会丢;日志天然完整、丢失少,但文件膨胀快、恢复要逐条回放。

1.2 选型要回答的五个问题

问题RDBAOF
最坏丢多少数据取决于快照间隔,默认配置可能丢几分钟everysec 最多约 1~2 秒
文件体积小,紧凑二进制+压缩大(命令文本),靠重写压缩
重启恢复速度快,直接反序列化慢,回放命令(大文件可能慢数倍)
对在线性能的影响bgsave 时 fork + 写盘持续写盘 + 重写时 fork
文件可读性/可修复二进制不可读命令文本可读,redis-check-aof 可截断修复

二、RDB:给内存拍一张二进制快照

2.1 触发方式:save 与 bgsave 的生死区别

# ✗ save:主线程自己生成快照,期间阻塞所有请求,生产环境永远不要用
127.0.0.1:6379> SAVE
OK

# ✓ bgsave:fork 出子进程在后台生成快照,主进程继续服务
127.0.0.1:6379> BGSAVE
Background saving started

自动持久化规则触发的实际都是 bgsave:

# redis.conf:save <秒数> <写操作次数>,任一条件满足即触发 bgsave
save 3600 1      # 3600秒(1小时)内至少1次写入
save 300 100     # 300秒(5分钟)内至少100次写入
save 60 10000    # 60秒内至少10000次写入
stop-writes-on-bgsave-error yes   # 快照失败后拒绝写入,防止"以为持久化正常"
rdbcompression yes                # LZF压缩,省磁盘,CPU开销很小
rdbchecksum yes                   # 文件末尾CRC64校验
dbfilename dump.rdb
dir /data/redis                   # RDB和AOF文件目录,生产务必挂独立数据盘

不想用 RDB 时显式关闭(注意不是留空,留空在某些版本含义模糊):

save ""

2.2 bgsave 为什么能"边服务边备份":fork + 写时复制

这是 RDB 最核心的机制,也是理解其性能影响的关键:

主进程
  │ fork() 系统调用(复制页表,不复制数据,速度取决于内存映射规模)
  ├───────────────── 子进程(与主进程内存映像完全一致的副本)
  │                    遍历内存,把"fork 那一刻"的数据写入临时 RDB
  │                    写完 fsync → 原子 rename 替换旧 dump.rdb
  │
  └─ 主进程继续处理写请求
       写某页时,操作系统触发 COW(Copy-On-Write):
       复制该物理页给主进程改新页,子进程看到的仍是旧页(快照一致性)

COW 让父子进程逻辑上各持一份内存、物理上共享未修改的页,快照期间内存增量只等于"被修改的页大小",而不是整份数据。但它也带来三个生产中必须面对的成本(第六章细讲):

  1. fork 本身会短暂阻塞主线程:要复制页表,10GB 实例通常几十毫秒,内存越大、页表越大,极端可达秒级。
  2. 快照期间大量写入会放大内存:被 COW 复制的页越多,额外内存越高(最坏接近数据集大小),大实例 + 大量写入可能触发 OOM。
  3. 透明大页(THP)必须关闭:开启时 COW 复制单位从 4KB 变成 2MB,写一页复制 2MB,内存放大和卡顿都会恶化。

2.3 RDB 的数据安全窗口

快照是离散的点,两次快照之间宕机,中间数据全丢。用默认规则算笔账:

低峰期(每分钟只有几十次写)
  → 永远触发不了 save 60 10000
  → 只能靠 save 300 100 / save 3600 1
  → 最坏丢失窗口接近 5 分钟~1 小时   ← 开篇事故的根因

高峰期
  → 每 60 秒左右一次快照,最坏丢约 1 分钟

另外 LASTSAVE 和 INFO persistence 可以确认最近一次成功快照的时间:

127.0.0.1:6379> INFO persistence
rdb_last_bgsave_status:ok
rdb_last_save_time:1727510400          # 最近一次RDB成功时间
rdb_last_bgsave_time_sec:3             # 上次bgsave耗时
rdb_changes_since_last_save:1832       # 上次快照后的写入数
latest_fork_usec:87231                 # ★ 上次fork耗时(微秒),监控大实例必看

2.4 RDB 适合做什么

  • 灾难恢复的冷备文件:紧凑、单文件,定时把 dump.rdb 传到对象存储/异地,恢复时拉回来启动即可。
  • 全量同步的载体:主从全量复制时主节点生成 RDB 发给从节点(无盘复制除外)。
  • 可整体重建的纯缓存:数据丢了能从数据库/上游重新预热的场景,RDB 足够。
  • 不适合:丢失窗口内有无法重建的状态(支付状态机、分布式锁之外的凭证类数据)。

三、AOF:把每条写命令追加到日志

3.1 工作流程:命令执行后才写日志

AOF 的写入方向与 MySQL 的 redo log 不同,是先执行命令、再记日志:

客户端写命令
  → 主进程执行命令(数据先变)
  → 命令协议文本追加到 AOF 缓冲区(aof_buf,用户态内存)
  → 根据 appendfsync 策略刷到内核页缓存/磁盘

"先执行后记录"的好处是不会阻塞命令执行、日志里不会有语法错误的命令;代价是写完命令、还没刷盘时宕机,这部分会丢——丢多少由 fsync 策略决定。

3.2 三种刷盘策略:always / everysec / no

appendonly yes
appendfilename "appendonly.aof"        # Redis 6 单文件;7.x 实际写在 appendonlydir/ 下
appendfsync everysec                   # ★ 三种策略的核心开关
no-appendfsync-on-rewrite no           # 重写期间是否暂停fsync,见 4.4
auto-aof-rewrite-percentage 100        # 文件比上次重写后增长100%触发重写
auto-aof-rewrite-min-size 64mb         # 文件至少64mb才考虑重写
aof-load-truncated yes                 # 恢复时遇到尾部损坏自动截断
策略刷盘时机最坏丢失写入性能(参考量级)适用
always每条写命令都 fsync 后才返回几乎为零约几百~2000 QPS,性能下降一个数量级以上极端数据安全、写入量极低
everysec(默认推荐)后台线程每秒 fsync 一次约 1~2 秒接近无持久化,2万~5万+ QPS绝大多数业务
no完全交给操作系统(通常约30秒)约 30 秒,且不可控最高基本没人显式使用

关于 everysec 一个容易忽略的实现细节:fsync 由独立后台线程每秒执行,主线程只管把命令 write 到内核缓存,所以正常情况下主线程不等磁盘;但如果某次 fsync 卡住超过 2 秒(磁盘繁忙),主线程为保证日志顺序会短暂阻塞等待——这就是 Redis 单实例上磁盘延迟可能传导到请求延迟的唯一路径。监控上看 aof_delayed_fsync 计数器:

INFO persistence
aof_enabled:1
aof_last_write_status:ok               # AOF写入状态
aof_delayed_fsync:0                    # ★ 大于0说明磁盘已经跟不上、主线程被拖慢
aof_last_bgrewrite_status:ok
aof_current_size:8126464               # 当前AOF大小
aof_base_size:4194304                  # 上次重写后的基准大小(两者比值决定何时再重写)

3.3 AOF 恢复:回放命令

启动时若同时存在 RDB 和 AOF,Redis 优先加载 AOF(因为它的数据更完整)。AOF 回放是逐条执行写命令,同样的数据量,恢复时间通常是 RDB 的 3~10 倍(混合持久化就是为了解决这个问题,第五章讲)。

AOF 是文本协议(RESP),出问题可以直接看、可以修:

# 宕机导致文件尾部写了半条命令,先备份再用官方工具检查
cp appendonly.aof appendonly.aof.bak
redis-check-aof --fix appendonly.aof   # 截断尾部不完整命令,数据损失仅限最后半条

这是 AOF 相对 RDB 的运维优势:RDB 文件结构损坏基本无法手工救,AOF 只丢尾部一点点。


四、AOF 重写:bgrewriteaof 怎么给日志"瘦身"

4.1 不重写会怎样

一个 key 被 INCR counter 了 10 万次,AOF 里就老老实实记了 10 万行 INCR;一个 key 设了又删(SET k 1 ... DEL k),日志里留下两条早已无意义的命令。AOF 文件会无限膨胀,大部分内容对"重建当前数据"毫无价值。

AOF 重写(rewrite)解决的就是这个问题:不读旧日志,直接遍历当前内存,用尽可能少的命令重新描述数据集。

旧 AOF(10万行)                重写后的 AOF(1行)
INCR counter  →  ...×100000     SET counter 100000
SET k 1                        (k 已删除,整个key不出现在新文件)
...
DEL k

4.2 重写流程:fork 子进程 + 重写缓冲区双写

主进程收到 BGREWRITEAOF(或自动触发)
  │ fork() 子进程
  │
  ├─ 子进程:遍历内存数据集
  │    生成描述当前状态的最少命令 → 写临时 AOF 文件
  │
  └─ 主进程继续服务,重写期间的新写命令同时写两处:
       ① 旧 AOF 缓冲区(保证重写失败时旧日志完整)
       ② aof_rewrite_buf(重写缓冲区,记录重写期间增量)

子进程写完
  → 主进程短暂阻塞(毫秒级):把 rewrite 缓冲区的增量追加到新文件
  → fsync → 原子 rename 新文件替换旧 AOF

关键点:重写不是对旧文件做压缩,而是对内存做一次全新快照式转储,所以与旧 AOF 当前多大无关,只与数据集大小有关。主进程最后的"追加增量 + rename"是全程唯一的短暂阻塞点。

4.3 自动触发参数

# 当前AOF大小比"上次重写完成时的大小"增长 100%(即翻倍)且 ≥64MB 时自动重写
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

刚启动没有"基准大小"时,Redis 以 AOF 文件的初始加载大小为基准。也可以手动触发:

127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started

bgsave 和 bgrewriteaof 不会同时执行:如果一个正在跑,另一个会排队,避免双倍 COW 内存。这也是监控大实例持久化任务时要关注的点(Redis 6.2+ 对两者调度有优化,但仍互相影响节奏)。

4.4 重写期间的两个参数取舍

# 重写期间主进程是否对旧AOF停止fsync
no-appendfsync-on-rewrite no
  • no(默认):重写期间照常每秒 fsync,数据安全,但重写 IO + fsync IO 叠加,磁盘繁忙时可能拖慢主进程。
  • yes:重写期间不 fsync,减轻磁盘压力,但重写的几十秒~几分钟内宕机会丢较多数据。除非磁盘 IO 确实扛不住,否则保持 no。

内存账也要提前算:重写期间的"新 AOF 文件 + 旧 AOF 文件 + rewrite 缓冲区"三份数据共存,加上 fork 的 COW,磁盘至少预留数据集 2 倍以上空间,内存预留 20%~50% 余量,否则重写高峰可能写满磁盘或触发 OOM。


五、混合持久化:RDB 的头 + AOF 的尾

5.1 它解决了 AOF 恢复慢的痛点

Redis 4.0 引入、5.0 默认开启的混合持久化(aof-use-rdb-preamble),让 AOF 重写生成的新文件不再是纯命令文本,而是"RDB 格式的全量底座 + 一小段 AOF 增量命令":

混合 AOF 文件结构
┌──────────────────────────────────────┐
│ RDB 二进制:重写时刻的完整数据集快照   │  ← 占文件99%体积,加载走快速反序列化
├──────────────────────────────────────┤
│ AOF 命令:重写之后的增量写命令         │  ← 只有很短的尾部,逐条回放
└──────────────────────────────────────┘

配置(Redis 5.0+ 默认就是 yes,建议显式写出):

aof-use-rdb-preamble yes

加载时:前半段当 RDB 快速反序列化,尾部 AOF 增量回放。既拿到了 AOF 的低丢失(最多 1 秒),又拿到了 RDB 的快恢复——一个 10GB 数据的实例,纯 AOF 恢复可能要十几分钟,混合格式通常一两分钟。

5.2 Redis 7.0 的 Multi-Part AOF(MP-AOF)

Redis 7.0 把单个大 AOF 文件拆成了目录下的多文件结构,混合格式成为基础形态,重写不再需要"暂停追加增量"的旧机制:

dir/appendonlydir/
├── appendonly.aof.manifest                    # 清单:记录当前有效文件集合
├── appendonly.aof.1.base.rdb                 # BASE:RDB全量底座(混合持久化)
├── appendonly.aof.1.incr.aof                 # INCR:base之后的增量命令,持续追加
└── (重写后旧版本进入 history,安全保留后清理)
  • 重写时生成新版本的 base,旧的 base+incr 被标记为 history,新 incr 从头开始——新旧版本在磁盘上并存,通过 manifest 原子切换,杜绝了旧版"重写中途崩溃可能损坏唯一 AOF"的风险。
  • 7.0 后 appendfilename 仍是逻辑前缀,实际文件都在 appendonlydir/ 目录;运维备份要备份整个目录和 manifest,不能只拷单个文件。
  • Redis 8.x 延续这套结构并增强了 I/O 线程与副本侧持久化能力,配置语义不变。

六、大实例的持久化成本:fork 卡顿怎么治

RDB 和 AOF 重写都要 fork,在数据量小的时候无感,实例到了 10GB+ 就成了头号性能问题。理解成本构成后,治理手段很明确。

6.1 fork 耗时看什么

INFO persistence
latest_fork_usec:87231      # 最近一次fork耗时,单位微秒。87231 ≈ 87ms

经验阈值:稳定低于 100ms 健康;持续超过 200ms 需要治理;超过 1s 的大实例必须拆分或上无盘方案。 fork 耗时主要与内存映射的页表规模相关,也和机器内存碎片化有关。

6.2 治理清单

措施说明
关闭 THPecho never > /sys/kernel/mm/transparent_hugepage/enabled,写进 rc.local/启动脚本;否则 COW 以 2MB 为单位,内存放大数百倍
允许内存过量分配sysctl vm.overcommit_memory=1,保证 fork 不会因"看似内存不足"失败(Redis 官方明确要求)
预留 COW 内存持久化实例内存占用不超过物理内存的 50%~60%,给快照期间写入留复制空间
控制单实例大小业务实例建议 ≤ 10~20GB;更大的数据做分片(Cluster),fork 耗时随内存近似线性增长
错峰持久化多个大实例在同一台物理机时,错开 bgsave/重写时间,避免 fork 和 IO 叠加
用更快的磁盘AOF everysec 的 fsync 延迟直接受磁盘影响,NVMe SSD 是标配;aof_delayed_fsync 要盯住
监控latest_fork_usec、rdb_changes_since_last_save、aof 重写耗时、磁盘 inode/容量

6.3 从节点的特殊用法

持久化任务放在从节点也是常见架构:主节点关闭 RDB/AOF(追求最低延迟),让一个专用从节点开混合持久化负责备份,并把多个从节点的持久化调度时间错开,避免 fork 和磁盘 IO 叠加。代价是故障切换后新主没有本地持久化文件——所以至少要保留一个开启持久化的副本,且冷备文件定时上传对象存储,不能让整个拓扑在持久化上裸奔。


七、选型决策:四种典型场景对号入座

你的 Redis 数据丢了会怎样?
│
├─ 完全无所谓,能从MySQL/上游全量重建(纯缓存、热点数据、排行榜临时值)
│    → 关闭持久化(save "" / appendonly no),或仅保留低频 RDB 做快速预热
│      配合主从副本防单点故障即可
│
├─ 丢几秒可以接受,但不能丢几分钟~几小时(绝大多数业务:会话、状态机、限流计数)
│    → ★ 混合持久化:appendonly yes + everysec + aof-use-rdb-preamble yes
│      同时保留 RDB 定时冷备(默认 save 规则即可)上传异地
│
├─ 一条都不能丢(资金凭证、无法重建的审计状态)
│    → 先反问:这种数据为什么在 Redis?应迁到数据库/带事务的存储
│      确需保留:everysec + 多副本 + wait 命令确认写入副本数;
│      always 只用于极低写入量场景,做好压测
│
└─ 跨机房容灾/长期归档需求
     → 定时 bgsave 产物 + AOF 目录定期上传对象存储(RDB单文件最适合冷备)
       主从/Cluster 解决高可用,文件备份解决灾难恢复,两者不互相替代

完整生产配置(混合持久化基线,Redis 6.2/7.x 通用)

# ===== RDB:保留快照,作为冷备与全量同步载体 =====
save 3600 1
save 300 100
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb

# ===== AOF:数据安全主角 =====
appendonly yes
# Redis 7:appendfilename 是逻辑名,文件落在 appendonlydir/ 目录
appendfilename "appendonly.aof"
appendfsync everysec
aof-use-rdb-preamble yes              # 混合持久化(5.0默认yes,建议显式)

# ===== 重写策略 =====
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
no-appendfsync-on-rewrite no          # 磁盘不是极端紧张就保持no
aof-load-truncated yes

# ===== 目录:独立数据盘 =====
dir /data/redis

三种持久化形态总对比

维度仅 RDB仅 AOF(everysec)混合持久化(推荐)
最坏丢失分钟级(低峰更久)1~2 秒1~2 秒
文件大小最小最大(重写后仍含命令文本)较小(RDB 压缩底座)
恢复速度最快最慢(回放命令)接近 RDB
文件可修复性差好(文本截断)底座不可读,尾部可截断
在线性能影响fork + COWfsync + 重写 fork两者都有,但频率/体积更优
典型用途冷备、全量复制、可重建缓存历史兼容/纯命令日志需求生产默认形态

八、常见问题

8.1 RDB 和 AOF 同时开启,重启时加载哪个?

优先加载 AOF(混合格式时加载混合 AOF),因为它的数据更完整;AOF 关闭或不存在时才加载 dump.rdb。所以运维要注意:你以为关了 AOF 就回到 RDB 数据,但 appendonly yes 的状态没变,旧 AOF 文件还在时重启仍会加载它。切换持久化形态后要同步处理数据目录里的旧文件,并做好备份。

8.2 everysec 真的最多丢 1 秒吗?

标称是"最近 1 秒",工程上按 1~2 秒预期:宕机发生在 fsync 前的临界时刻、或 fsync 因磁盘繁忙延迟(aof_delayed_fsync > 0),丢失窗口会略大于 1 秒。注意它保证的是"日志落盘的时间差",不是业务一致性——涉及多 key 的操作如果没用事务/Lua,回放后可能停在部分完成状态,数据一致性仍由业务设计保证。

8.3 为什么 bgsave/重写时内存涨了很多甚至 OOM?

fork 后父子共享物理页,主进程每改一页 COW 复制一页。大实例在快照期间如果有大量写入(如全量过期淘汰、批量导入),复制内存可能接近数据集大小;开启 THP 时按 2MB 大页复制,放大更严重。治理:关 THP、实例内存用到物理内存 60% 以下、大写入任务避开持久化窗口、latest_fork_usec 和内存曲线一起监控。

8.4 AOF 文件一直不重写/重写太频繁怎么办?

不重写检查三件事:① 文件是否达到 auto-aof-rewrite-min-size(默认 64MB);② 增长比例是否达到 auto-aof-rewrite-percentage;③ 是否已有 bgsave/重写在跑(互斥排队)。重写太频繁通常是 percentage 太小或写入模式产生大量冗余命令(如高频 INCR/EXPIRE 刷新),可把百分比调到 200,并用 INFO 里的 aof_current_size 与 aof_base_size 比值观察真实增长曲线,不要拍脑袋调。

8.5 主从架构下,持久化应该放在主还是从?

高可用与持久化是两件事:主从复制防的是节点宕机,RDB/AOF 防的是整个数据集损坏/误删(FLUSHALL、逻辑错误会同步到所有从节点!)。推荐组合:主从都开混合持久化或至少主开 AOF;再安排定时 bgsave + 异地冷备防逻辑误删;让一个延迟不敏感的从节点错峰承担持久化 IO 可以降低主节点抖动,但不能让全集群无持久化。另外善用 rename-command 在生产禁用/改名 FLUSHALL、FLUSHDB、CONFIG、KEYS 等危险命令。

8.6 持久化能替代数据库吗?

不能。Redis 持久化是"崩溃恢复"机制,不是数据库的 ACID 保障:everysec 有丢失窗口、RDB 有分钟级窗口、AOF 回放不保证跨 key 事务一致性、备份频率决定 RPO。正确姿势是"能重建的走缓存,丢不得的落数据库,Redis 只存加速用的副本或可容忍秒级丢失的运行态",开篇那种把无法重建的支付状态只放 Redis 的设计,再好的持久化配置也只是缓解,不是根治。


九、总结

机制速查卡

【RDB 快照】记录"结果"
  触发:BGSAVE(fork子进程,生产禁用SAVE)/ save规则 / shutdown / 全量复制
  原理:fork + COW,子进程转储内存为紧凑二进制 dump.rdb
  优点:文件小、恢复快、适合冷备和全量同步
  弱点:离散快照,默认规则最坏丢几分钟~1小时
  监控:rdb_last_bgsave_status、rdb_changes_since_last_save、latest_fork_usec

【AOF 日志】记录"过程"
  流程:命令先执行 → 追加 aof_buf → 按策略 fsync
  always:逐条刷盘,几乎不丢,QPS 降一个数量级
  everysec(推荐):后台线程每秒刷盘,最坏丢1~2秒,性能接近全开
  no:交给OS,约30秒且不可控
  优点:丢失少、文本可读、redis-check-aof 可截断修复
  弱点:文件大、恢复慢(逐条回放)

【AOF 重写】遍历内存生成最少命令,与旧日志无关
  触发:BGREWRITEAOF / 增长100%且≥64MB
  流程:fork子进程转储 + 主进程双写rewrite缓冲区 → 追加增量 → 原子替换
  bgsave 与 bgrewriteaof 互斥;磁盘留2倍、内存留余量

【混合持久化 4.0+ / 5.0默认】aof-use-rdb-preamble yes
  AOF文件 = RDB全量底座 + 少量AOF增量 → 恢复快 + 丢失少,生产默认形态
  Redis 7.0 MP-AOF:appendonlydir/ 下 base.rdb + incr.aof + manifest,
                    新旧版本并存原子切换,备份要备份整个目录

【大实例治理】
  关 THP;vm.overcommit_memory=1;单实例≤10~20GB;
  latest_fork_usec <100ms;NVMe盘;盯 aof_delayed_fsync

给团队的建议

项建议
默认形态混合持久化 + everysec,除非能明确论证数据可全量重建
纯缓存可关 AOF,但保留低频 RDB 加速重启预热,冷备上传对象存储
操作系统THP=never、overcommit=1、独立数据盘、预留 COW 内存
容量规划磁盘 ≥ 数据集 3 倍(新旧 AOF + RDB 共存),内存使用 ≤60%
监控告警latest_fork_usec、aof_delayed_fsync、持久化失败状态、磁盘空间
安全rename-command 禁用 FLUSHALL/FLUSHDB;记住复制不防误删,冷备才防
升级 7.x备份对象从单文件变为 appendonlydir 整个目录,运维脚本要同步改

一句话

RDB 和 AOF 的全部差异都源自一个哲学分野:RDB 记录"某一刻内存里的结果",用 fork + 写时复制把内存转储成紧凑二进制快照,所以文件小、恢复快,适合冷备和主从全量同步,但离散快照之间天然有分钟级丢失窗口;AOF 记录"数据变成这样的过程",命令先执行后追加、每秒由后台线程 fsync,所以最坏只丢 1~2 秒且文本可修复,但日志无限膨胀、恢复要逐条回放;bgrewriteaof 的精妙之处在于它不压缩旧日志而是 fork 子进程遍历当前内存、用最少命令重新描述数据集,重写期间主进程靠 rewrite 缓冲区双写保住增量,最后原子替换;混合持久化则是把两者拼起来——AOF 文件以 RDB 二进制做全量底座、只留尾部一小段增量命令,重启时大部分走快速反序列化、小部分回放,同时拿到了 RDB 的快恢复和 AOF 的低丢失,Redis 7.0 的 Multi-Part AOF 又把它演进成 base.rdb + incr.aof + manifest 的目录化原子切换结构。选型时不要在 always 和关闭持久化两个极端之间摇摆:能整体重建的纯缓存用低频 RDB 甚至不持久化、丢不得的业务数据本来就不该只放 Redis,而 95% 的生产场景答案明确——混合持久化加 appendfsync everysec,再配上关闭 THP、overcommit=1、单实例不超过 20GB、latest_fork_usec 和 aof_delayed_fsync 监控、以及定时上传异地的 RDB 冷备;数据安全与性能不是二选一,理解每种机制把成本花在了哪里(fork、COW、fsync、重写双写),才能用 1 秒丢失窗口的代价换回几乎无损的吞吐。

互动话题:你们线上 Redis 开的是 RDB、AOF 还是混合持久化?有没有遇到过 fork 卡顿导致请求抖动、或者 AOF 重写把磁盘写满的情况?支付/订单状态这类"丢不得又想放 Redis"的数据,你们最终是怎么设计的?评论区聊聊你的持久化配置和踩坑经历。


参考资料


标题:Redis 持久化 RDB vs AOF 怎么选?——数据安全 vs 性能
作者:jiangyi
地址:http://www.jiangyi.space/articles/2026/10/05/1791036172592.html
公众号:服务端技术精选
    评论
    0 评论
avatar

取消