目录
正在加载目录…
专栏文章
专栏文章
Redis 专栏
1. Redis:核心数据结构与应用场景 2. Redis 数据结构:底层实现与性能取舍 3. Redis 持久化与复制:RDB、AOF 与主从同步 4. Redis 缓存设计:穿透、击穿与雪崩治理 5. Redis 高可用:哨兵、集群与故障转移 6. Redis 多级缓存:一致性、失效与性能优化

Redis 持久化与复制:RDB、AOF 与主从同步

发布于 2026-07-07 10:09 · 最后编辑于 2026-07-31 15:52 · 字数 1,870 👁 80 次阅读

深入 Redis 数据安全的两条主线:持久化(RDB/AOF 的工作原理与选择)和主从复制(全量同步/增量同步/断点续传),以及实践中的常见坑。

目录

章节说明
AOF 日志工作原理写后日志、三种刷盘策略、AOF 重写机制
RDB 快照工作原理fork + Copy-on-Write、快照频率权衡
混合持久化4.0+ 兼顾两者优势
主从复制原理全量同步三阶段、增量同步、repl_backlog
主从复制的坑数据不一致、主从切换的典型问题
AOF 重写机制重写缓冲区的作用与新写操作处理

AOF 日志工作原理

redis persistence

写后日志(Write After Log)

AOF 是先执行命令,后写日志(与数据库 WAL 的"先写日志再执行"相反):

客户端命令 → Redis 执行 → 写入 AOF 缓冲区 → 刷盘

好处

  1. 命令已被验证合法,日志中不会有错误的命令
  2. 不阻塞当前命令的执行(日志写在执行之后)

坏处:执行成功但还未写入 AOF 就宕机 → 这段数据丢失

三种刷盘策略

appendfsync触发时机数据安全性性能
always每条命令都同步刷盘最高(最多丢 1 条)最低
everysec推荐每秒刷盘一次高(最多丢 1 秒)
no由 OS 决定最高

生产推荐 everysec:兼顾安全性和性能,每秒刷盘由后台线程异步执行,不阻塞主线程。

AOF 文件可能的阻塞点

  • always 模式:每次命令都刷盘,fsync 调用频繁
  • everysec 模式:后台线程正在 fsync 时,主线程再次调用 write 会等待上一次 fsync 完成

RDB 快照工作原理

BGSAVE 与 fork

主进程 → fork() → 子进程(负责将内存数据序列化写入 dump.rdb)
                   主进程继续处理请求

Copy-on-Write(写时复制)

  • fork 后父子进程共享同一份内存页
  • 主进程修改某个内存页时,OS 复制该页给主进程,子进程继续读原始页
  • 子进程看到的始终是 fork 时刻的内存快照

快照频率的权衡

频率高 → 数据丢失少 → 但 fork 开销大,可能阻塞主线程
频率低 → fork 开销小 → 但宕机后恢复时间长,丢失数据多

fork 的开销:fork 本身是 O(1) 的(只复制页表,不复制数据),但数据量越大,页表越大,fork 越慢(通常几十毫秒到几百毫秒)。

RDB 文件格式(源码角度)

REDIS | db_version | databases | EOF | check_sum
 5B       4B           多个DB           8B

每个 DB:
  SELECTDB | db_number | key_value_pairs

每个 key_value_pair:
  [EXPIRETIME_MS + 过期时间] | type | key | value

可用 redis-check-rdb dump.rdb 验证 RDB 文件完整性。

混合持久化

Redis 4.0+ 引入,开启方式:

aof-use-rdb-preamble yes

AOF 文件结构

[RDB 格式的全量数据] + [增量 AOF 日志]

AOF 重写时,将当前内存数据以 RDB 格式写入文件头部,后续增量变更以 AOF 格式追加。

优势

  • 恢复速度接近 RDB(头部是紧凑的二进制格式)
  • 数据安全性接近 AOF(尾部的增量日志)
  • 生产环境推荐开启

主从复制原理

redis replication

全量同步(第一次同步)三阶段

阶段1:建立连接
  从库 → 主库:PSYNC ? -1(runID 未知,offset=-1)
  主库 → 从库:+FULLRESYNC {runID} {offset}

阶段2:主库发送 RDB
  主库执行 BGSAVE 生成 RDB,同时将新写入命令存入 replication buffer
  主库将 RDB 文件发送给从库
  从库清空本地数据,加载 RDB

阶段3:发送增量命令
  主库将 replication buffer 中积累的命令发送给从库
  之后持续发送增量写命令

增量同步(断点续传)

从库重连后,发送 PSYNC {runID} {offset}

主库检查:
  1. runID 是否匹配(防止主库已切换)
  2. offset 是否在 repl_backlog(环形缓冲区)范围内

  如果两者都满足 → 增量同步(只发 offset 之后的命令)
  否则 → 全量同步

repl_backlog_buffer(复制积压缓冲区)

  • 主库维护的固定大小环形缓冲区(默认 1MB)
  • 保存最近写入的命令,供从库断连重连后做增量同步
  • 如果 offset 对应的数据已被覆盖,必须做全量同步
# 适当调大,避免网络抖动时频繁全量同步
repl-backlog-size 10mb

复制相关配置

# redis.conf(从库)
replicaof 主库IP 主库端口
masterauth 主库密码

# 从库只读
replica-read-only yes

# 从库断连时是否继续服务旧数据(yes=服务过期数据,no=返回错误)
replica-serve-stale-data yes

# 调大复制积压缓冲区(防止短暂断连触发全量同步)
repl-backlog-size 10mb

主从复制的坑

坑 1:从库崩溃重启 → 触发全量同步

从库重启后 runID 改变,主库认为这是新实例,强制全量同步。

  • 影响:从库需要加载大量 RDB,期间无法服务读请求
  • 解决:Redis 4.0+ 支持 psync2,崩溃重启也能增量同步(通过持久化 replid 到磁盘)

坑 2:网络闪断 → repl_backlog 被覆盖 → 全量同步

网络断连超过 repl_backlog 能覆盖的时间,触发全量同步。

  • 解决:增大 repl-backlog-size

坑 3:主从不一致(过期 key)

从库上的 key 过期了,但在从库上查询可能还会返回(从库只执行 DEL 命令,不主动删除过期 key):

  • Redis 3.2+ 修复:从库会检查 key 的过期时间,已过期则返回空(即使还没收到主库的 DEL)

坑 4:主从切换时数据丢失

主库宕机,但部分数据还未同步到从库,新主库(原从库)没有这部分数据:

  • 解决min-replicas-to-write 1 + min-replicas-max-lag 10(至少 1 个从库确认,且延迟 ≤ 10 秒,才允许写入)

坑 5:主库生成 RDB 期间宕机

BGSAVE 的 RDB 文件未生成完,主库宕机,从库收到不完整的 RDB → 从库会拒绝加载(有校验和保护)。

AOF 重写机制

为什么需要重写

AOF 追加记录每一条写命令,会有大量冗余(如 INCR 执行了 100 次,AOF 有 100 条,但只需要 1 条 SET)。重写后只保留能恢复当前数据状态的最小命令集。

重写流程

主进程 fork() 子进程
  ↓
子进程:读取当前内存数据,生成重写后的新 AOF 文件
  ↓(同时)
主进程:继续处理请求,将新写入命令追加到:
  1. 旧 AOF 文件(保证极端情况下旧文件可用)
  2. AOF 重写缓冲区(新写入命令)
  ↓
子进程完成 → 主进程将重写缓冲区的命令追加到新 AOF 文件
  ↓
原子替换旧 AOF 文件(rename 系统调用)

AOF 重写缓冲区(aof_rewrite_buf)是关键:确保重写期间新写入的命令不丢失,且最终被合并进新文件。

触发条件

# 自动触发(两个条件同时满足)
auto-aof-rewrite-percentage 100  # 比上次重写后增长 100%
auto-aof-rewrite-min-size 64mb   # 文件大小至少 64MB

# 手动触发
BGREWRITEAOF

参考资料

← 返回列表

评论 (0)

暂无评论,来留下第一条吧。
登录注册 后才能发表评论