当前位置: 首页 > news >正文

从零起步学习Redis || 第七章:Redis持久化方案的实现及底层原理解析(RDB快照与AOF日志)

引言

Redis 是典型的内存型键值数据库。它的高性能、高吞吐、低延迟,离不开把数据主要放在内存中的设计。但纯内存也意味着:“断电 / 进程崩溃 / 机器重启”可能造成全部数据丢失。

为了兼顾“内存速度”与“数据持久性”,Redis 提供了持久化机制,将内存中的数据写到磁盘,以便重启后恢复。Redis 目前主要支持两大类持久化方式:

  1. RDB — 快照方式

  2. AOF — 日志方式

在实际部署中,也常见将两者组合使用,以兼顾恢复速度与数据安全性。

本文将从原理、实现流程、优缺点、演化(比如 Redis 7.0 引入的混合方式)等角度,深入剖析 RDB 与 AOF。

一、RDB:快照式持久化

1.1 原理

  • RDB 是 Redis 的“整体快照”持久化方式。它会在某些触发条件下(定期或手动触发)将当前内存中的整个数据集(键、值、数据结构等)做一次“拍照”,序列化并写到一个二进制文件(默认叫 dump.rdb)。

  • 启动时,Redis 会读取该 RDB 文件,将其中的内容反序列化到内存,从而恢复数据状态。

  • 为了避免持久化操作阻塞主线程(影响响应),Redis 通过 fork() 创建子进程来执行序列化和写文件(临时文件),父进程继续响应客户端;子进程完成后再将临时文件重命名 / 覆盖正式文件,实现原子性替换。

  • 序列化格式遵循 Redis 的内部 RDB 格式(包括类型标识、压缩、校验等)以节省空间和加快恢复过程。

Redis 官方文档中对此有明确说明。

1.2 实现流程(典型流程)

下面是简化后的 RDB 快照流程:

  1. 触发快照

    • 配置文件 redis.conf 中有 save 条目(例如 save 900 1,意思是 900 秒内至少有 1 次写操作就做快照;也可有多个触发条件)

    • 手动执行 BGSAVE 命令

    • 注意:有一个 SAVE 命令也是可以用来做快照,但 SAVE 是同步操作,会阻塞服务器主进程,一般不推荐在生产环境中使用。

  2. fork 子进程

    • 父进程继续处理客户端请求

    • 子进程在其自己的地址空间里遍历当前内存数据,并做序列化、压缩、写磁盘等工作

    • 操作系统利用 Copy-On-Write(写时复制)机制确保父子进程对同一内存页共享,直到某一方写入时才真正复制页面

  3. 子进程序列化写临时文件

    • 将内存中的数据以 RDB 格式序列化写入临时文件(如 temp-db.rdb

    • 完成后做 fsync(确保写入磁盘)

  4. 原子替换

    • 子进程成功写出、校验无误后,将临时文件重命名 / 移动 / 覆盖为最终的 dump.rdb 文件

    • 子进程退出

  5. 父进程监控 / 完成

    • 父进程得知快照完成(通过信号或状态)

    • 后续根据下次触发条件再重复上述流程

  6. 重启恢复

    • 启动 Redis 时,如果检测到已有的 dump.rdb 文件(路径、名称符合配置),则读取它,反序列化,将数据加载入内存

    • 加载过程中 Redis 会构建内存结构(hash, list, set, zset 等),并完成校验

在这整个过程中,父进程在 fork 操作(创建子进程)那一刻会经历短暂暂停(系统调用开销、页表复制、内核态切换等),在大数据量时可能造成较为明显的延迟或卡顿。

1.3 问题:如果在RDB快照式持久化过程执行 bgsave 过程中,redis要写数据,具体是如何做到?

  • Redis 在 BGSAVEfork 出子进程,子进程负责遍历内存、序列化写入磁盘;父进程继续响应客户端请求,包括写操作。

  • 通过操作系统提供的 Copy-On-Write (COW) 机制,当父进程尝试写某个内存页时,操作系统会将该页拷贝(给父进程独立页)后再写,这样子进程仍能看到快照时那一刻的旧页内容。

  • 因此写操作与快照是并行的:写操作不会破坏子进程的读取视图。子进程快照看到的是触发 fork 那一刻的“静态视图”,父进程写入的是后来变化的内容,隔离开来。

那么父进程修改的数据岂不是不会写入快照了?是的

那么修改的数据如何处理?等待下一次快照或者其他方案

1.4 优点

  • 恢复速度快:因为重启时只需加载一个紧凑、压缩后的二进制文件,而不用逐条执行命令,尤其对大数据量更有优势。

  • 存储文件体积小:RDB 文件是压缩/序列化后的整体快照,通常比 AOF 日志小很多。

  • 对运行时性能影响较小:除去 fork 时短暂开销外,正常运行时父进程几乎不用参与磁盘 I/O。

  • 适合用于备份 / 冷备份 / 远程复制:RDB 的快照文件作为离线备份、灾备恢复比较方便(整体文件便于传输、归档)。

  • 在副本(Replica / Slave)场景中优势:子节点做部分同步(partial sync)时,可以先发 RDB 快照,再同步增量。Redis 在复制机制中对 RDB 支持良好。Redis+2memurai.com+2

1.5 缺点 / 风险

  • 数据丢失窗口:因为快照是以时间/写次数触发的,如果 Redis 在两次快照之间发生崩溃或断电,则最近一段写入会丢失。

  • fork 开销 / 卡顿:对于大内存应用,fork 和写大快照可能引起明显延迟、内存页写时复制压力、系统抖动。

  • 快照过程资源开销:子进程需要遍历全量数据、做序列化及磁盘写入,对磁盘 I/O 资源、内存带宽、CPU 都有一定占用。

  • 快照频繁可能影响性能:若触发条件配置得太激进,会导致频繁做快照,对系统影响不容忽视。

二、AOF:日志式持久化

2.1 原理

  • AOF(Append Only File)以 操作日志 的方式做持久化:Redis 在执行每一个会改变数据的写命令(例如 SETDELHSETLPUSH 等)之后,把这条命令(以 Redis 协议格式)追加写入 AOF 日志文件。

  • 重启时,Redis 读取 AOF 文件并依次执行这些命令,以重建出数据集状态。

  • 为避免日志无限增长、重放命令开销过大,Redis 引入 AOF 重写(rewrite / compaction) 机制:在后台生成更简洁的日志(即只记录达到当前状态所需的最小命令)并替换旧日志。

  • 对于何时把日志刷盘(fsync),Redis 提供三种策略,以平衡性能与数据安全性:

    • appendfsync always:每次写命令都同步刷盘(最安全,但最慢)

    • appendfsync everysec:默认模式,每秒做一次 fsync(可能丢失最多一秒的数据)

    • appendfsync no:不主动 fsync,由操作系统决定何时落盘(性能最好,但安全性最低)

Redis 文档中对这些策略及其折中有明确说明。Redis+1

2.2 实现流程(简化版)

  1. 客户端发起写命令 → Redis 在内存中执行
    写命令(如 SET key value)先作用于内存数据结构

  2. 追加日志
    Redis 将该命令以 Redis 协议格式(RESP 协议风格)追加写入 AOF 缓冲区 / 文件末尾

  3. 根据策略刷盘(fsync)
    根据 appendfsync 设置,决定是否立即 fsync、每秒 fsync 或不 fsync

  4. 日志增长 & 重写触发
    随着命令增多,AOF 日志文件越来越大。为控制其体积、加速恢复,Redis 会在某些条件下触发 AOF 重写BGREWRITEAOF):

    • fork 子进程bgrewriteaof

    • 子进程根据当前内存中的 数据,生成对应的指令写入日志(即仅把当前状态所必需的操作写入)

    • 子进程与父进程同时记录增量命令(在重写期间,父进程继续处理写命令且写入旧日志和重写子进程)

    • 重写完成后,将新日志替换旧日志,清理旧文件

  5. 重启恢复
    在服务器启动时,Redis 加载 AOF 文件,按顺序重放命令,恢复到最新状态

注意一个重要点:Redis 的 AOF 机制是 “写后日志(write-after log)”——即先执行内存操作,再写日志;这样设计是为了避免记录无效命令(如果先写日志、再执行操作,那么如果操作失败,日志里可能留下不合法命令)。Redis+3ByteByteGo+3Redis+3

2.3 问题:如果在重写时,父/子线程一方要写数据,及具体如何实现?

首先要明确,子进程是如何共享父进程的数据的?

父进程在调用子进程时,操作系统会把父进程的页表复制一份给子进程,页表中记录了虚拟地址和物理地址的唯一映射关系,两者虚拟空间不同,但物理空间相同,而且此时进程的权限为只读

那我要往里面写数据,就会破坏权限,此时cpu触发写保护中断,修改权限为可读写,然后触发写时复制

在fork 子进程bgrewriteaof,会使用一个AOF重写缓冲区

在子进程完成重写操作后,会向父进程发送信号,此时

所以总结一下,过程如下

  • 当触发 BGREWRITEAOF(重写) 时,Redis fork 出子进程重写 AOF;

  • 在重写期间,主线程继续处理写命令,并把这些命令 写入旧 AOF 文件(正常追加);

  • 同时,主线程把这些写命令也记录到 重写缓冲区(diff buffer / rewrite buffer)

  • 子进程完成重写后通知父进程,父进程把重写缓冲区中的命令追加写到新 AOF 的末尾,以保证新日志包含重写期间的写入;

  • 最后,父进程做原子重命名 / 文件替换,切换到新的 AOF 日志,从此向新日志继续写命令。

2.4 优点

  • 更高的数据安全性 / 更小数据丢失:通常在 appendfsync everysec 模式下,最多丢失 1 秒内的数据;在 always 模式下理论上可做到写命令后即持久化。

  • 更可读 / 可审计:AOF 日志以命令格式记录(文本格式或类似 RESP 格式),可以人工阅读、审计、对比。

  • 日志追加式写入:追加写入本身具有良好的顺序写入特性,不易破坏已有内容。

  • 可修复 / 可跳过损坏部分:AOF 日志如果部分损坏,可以用 redis-check-aof 工具尝试修复或截断。

2.4 缺点 / 风险

  • 恢复速度慢 / 重放开销大:重启时需要执行整个日志中的所有命令。随着日志变大,恢复时间会变长,尤其在写很多命令、变更频繁的场景下。

  • 日志体积大:记录每条写命令,容易比 RDB 文件大很多,尤其在高写场景下。

  • 重写开销:AOF 重写要 fork 子进程、做日志重写、磁盘 I/O,有时可能影响性能。

  • 刷盘策略引入折中

    • always 策略带来显著的性能开销,写命令响应变慢;

    • no 策略牺牲安全性;

    • 默认的 everysec 是折中方案,但允许最多丢失一秒的数据。

  • 命令幂等性 / 再现性挑战:某些命令如果非幂等(例如 incr、append、时间相关命令等),重放时需确保重放逻辑和原始执行一致。

  • 日志损坏风险:虽然追加写入相对安全,但文件中间若损坏或未完整写入可能导致重放失败,需要工具处理。

三、RDB vs AOF:深入对比与权衡

下面是详细的对比与权衡建议,这对你在实际选择持久化策略时非常重要。

维度RDBAOF
一致性 / 丢失风险丢失可能为快照之间的时间窗口最多丢失 0–1 秒(everysec),或更少(always
重启恢复速度快(直接载入快照)慢(重放命令)
磁盘 / 存储空间较小(压缩快照)较大(日志全记录)
对运行时的影响较小,偶尔 fork 开销写命令、fsync、重写可能带来持续开销
实现 / 复杂性相对简单较复杂(重写机制、日志管理、恢复重放等)
可审计 / 可读性不可读(是压缩二进制文件)可读 / 可审计 / 可编辑(慎用)
适合场景缓存为主、可容忍数据丢失、需要快速重启业务数据为主、对数据安全性要求高、写操作频繁

Redis 官方文档和社区也推荐一种折中策略:RDB + AOF 混合(开启两者)——即用 RDB 做快照以加快恢复,用 AOF 记录写操作尽量减少丢失。

在 Redis 较新的版本中(如 7.x 之后),Redis 引入了一个混合持久化方式(Hybrid persistence / AOF-RDB 混合 / AOF preamble 机制),来折中两者优缺点。

3.1 混合方案工作原理

  • 重写 / 生成新的 AOF 时,不仅写文本命令日志,还在文件开头加入一个 RDB 快照前缀(preamble),使得新 AOF 文件前半部分是 snapshot 二进制、后半部分是日志命令。

  • 启动恢复时,Redis 先识别开头是 RDB 快照,先加载快照(高效),然后继续从日志部分重放命令(增量修正)。

  • 这样做能显著缩短重放命令的时间,同时仍保留了 AOF 的增量记录能力。

  • 在 Redis 7.0+ 中,这种混合方式通常默认启用(aof-use-rdb-preamble 配置项)

下面是混合方案文件结构示意:

+----------------------+-----------------------------+
| RDB 快照二进制区块     | 增量命令日志 (AOF 部分)        |
+----------------------+-----------------------------+

恢复过程:

  1. 识别 RDB 区块,加载快照

  2. 继续读取命令日志区块,按顺序重放

这种方式既保留了 fast snapshot 加载的优势,又控制了命令重放的压力。

5.2 优缺点与适用性

优点:

  • 恢复速度快:大部分数据通过快照加载,命令重放范围小

  • 日志体积受控:日志仅记录差异操作

  • 数据安全性仍较好:保留操作日志,可确保最近变更不丢失

  • 平衡性能与可靠性

缺点 / 注意事项:

  • 实现复杂度更高

  • 对配置和文件管理要求较高

  • 在某些老版本或不支持混合方案的环境上无法使用

  • 混合方式仍需考虑重写、刷盘策略、日志管理等

总之,如果你的 Redis 版本支持混合方案(7.x 以上),这是一个很值得考虑的折中策略。

http://www.dtcms.com/a/446938.html

相关文章:

  • python如何拼接图片
  • 云县网站建设找那家咸阳网站推广
  • 网站域名一年大概多少软件开发培训机构去哪个学校
  • VPS如何做镜像网站全网推广网站
  • 老年实训室建设介绍:打造“教-学-练”一体化老年实训建设样板
  • 布吉做棋牌网站建设哪家技术好淘外网站怎么做
  • 吃透链表进阶OJ:从 “怕踩坑” 到 “能讲透”
  • 国内做的比较大的外贸电商网站肇庆做网站设计
  • 重庆梁平网站建设哪家好crm系统排行榜
  • 备案信息修改网站负责人政务服务网站建设情况汇报
  • 南昌网站建设制作陕西省住建厅官网
  • 进步主义的异化:个人权利申索如何蜕变成圣母主义和功利主义
  • node-dommatrix
  • 人工智能赋能传统医疗设施设备改造:未来展望与伦理挑战
  • angular网站模板下载公众号必备50个模板
  • 移动端教学视频网站开发怎么制作网站?
  • 德惠市建设局网站商城 小程序
  • 图文网站源码做网站要源代码
  • 怎样下载别人网站自己做的视频html5企业网站模版
  • Git配置与安装并使用Git管理项目
  • 网站dns查询网址大全2345电脑版下载
  • 串扰13-串扰如何影响信号边沿
  • 泰安市住房与城乡建设局网站企业网站建设的目的
  • jetson nano搭建vue3环境
  • 为什么mysql要有主从复制,主库,从库这种东西
  • 进网站后台显示空白wordpress 虾米音乐插件
  • 中国最大的做网站公司常州企业建站系统
  • U支付自动发卡平台使用教程
  • 正规网站优化公司宝思哲手表网站
  • 山西做网站的公司哪个好阜城网站建设价格