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

【Redis】持久化方案——RDB和AOF

目录

一. 引入持久化

二.RDB

2.1.触发时机

2.1.1.手动触发

2.1.2.通过配置文件自动触发

2.1.3.RDB 持久化的数据丢失风险场景说明

2.2.RDB文件的处理

2.3.RDB效果演示

2.3.1.手动执行bgsave(自动触发关闭的时候)

2.3.2.插入新的key,不手动执行bgsave

2.3.3.bgsave / save的执行过程

2.3.4.通过配置自动生成rdb快照

2.3.5.如果把rdb文件故意改坏了,会怎么样?

2.4.RDB的优缺点

三. AOF

3.1.什么是AOF

3.3.使用AOF

3.4.AOF为什么快?

3.5.AOF的刷屏策略

3.6.AOF重写机制

四.关于 Redis 持久化加载的优先级

五.混合持久化

5.1.什么是混合持久化

5.2.见一见混合持久化


一. 引入持久化

MySQL 事务的核心特性 (ACID)

MySQL 事务有四个核心特性,通常称为 ACID 特性:

  1. 原子性 (Atomicity):事务是最小的执行单位,不可再分割。事务中的所有操作要么全部成功执行,要么全部失败回滚,不会存在中间状态。

  2. 一致性 (Consistency):事务执行前后,数据库必须处于一致的状态。这意味着事务必须遵循数据库的完整性约束(如主键、外键、唯一约束等),将数据库从一个有效状态转换到另一个有效状态。

  3. 隔离性 (Isolation):多个并发执行的事务之间应该相互隔离,防止它们互相干扰。一个事务的操作和使用的数据对其他并发事务是隔离的,防止出现脏读、不可重复读、幻读等问题。不同的隔离级别提供了不同程度的隔离保证。

  4. 持久性 (Durability):一旦事务成功提交(Commit),它对数据库所做的修改就是永久性的。即使系统发生故障(如宕机、重启),提交的结果也不会丢失。持久性本质上就是通过数据持久化来实现的

数据持久化的本质

  • 存储在硬盘上 => 持久:数据被写入非易失性存储设备(如硬盘、SSD)。即使进程终止或主机重启,数据依然存在。

  • 存储在内存上 => 不持久:数据保存在易失性存储器(RAM)中。一旦进程结束或主机断电/重启,内存中的数据就会丢失。

Redis 的持久化挑战与方案

Redis 的核心优势在于其极高的性能,这主要源于它是一个内存数据库——数据主要存储在内存中进行操作。然而,内存的易失性意味着仅靠内存无法保证数据的持久性(Durability)。

为了解决“既要速度(内存)又要持久(硬盘)”的矛盾,Redis 采用了内存+硬盘双存储的策略:

  1. 主工作区:内存:所有数据的读写操作都在超快的内存中进行,这是 Redis 高性能的基石。查询数据时直接从内存读取。

  2. 持久化保障:硬盘:Redis 会定期或按策略将内存中的数据以特定格式(如 RDB 快照 或 AOF 日志)持久化到硬盘上。

  3. 数据恢复:当 Redis 服务重启时,它会读取硬盘上的持久化文件,将数据重新加载到内存中,恢复服务中断前的状态。硬盘上的数据主要用于恢复,不直接用于服务期间的查询。

这种方案的要点与权衡:

  • 双写策略?并非每次数据写入都同步写内存和硬盘(那样会严重拖慢速度,失去内存优势)。Redis 提供了不同的持久化机制(主要是 RDB 和 AOF):

    • RDB (Redis Database):在指定时间间隔生成内存数据的快照文件。效率高,文件紧凑,但可能丢失最后一次快照后的数据。

    • AOF (Append Only File):记录每次写操作命令。数据安全性更高(可配置同步策略),文件更大,恢复可能较慢。通常两者结合使用。

  • 潜在的数据差异:由于持久化是异步或周期性进行的,在发生故障时,内存中最新的数据(尚未持久化到硬盘的)有可能丢失。RDB 和 AOF 的不同配置策略决定了数据丢失的风险窗口大小。追求更高持久性通常会牺牲一些性能。

  • 空间代价:同一份数据在内存和硬盘(至少一份持久化文件)上各存了一份。不过,考虑到硬盘存储成本相对较低,且 RDB 文件通常很紧凑,这个代价通常是可接受的,是换取持久性和性能平衡的必要开销。

  • 核心优势保留:Redis 对比 MySQL 等关系型数据库,最显著的优势依然是其极高的读写性能,这主要归功于其内存存储架构和高效的数据结构。持久化机制是在此基础上增加数据可靠性保障的关键补充。

RDB和AOF

Redis 主要提供两种持久化机制,它们的工作策略和适用场景有所不同:

  1. RDB (Redis Database) - 快照式持久化

    • 策略:在预定义的时间点(例如每隔 N 分钟/小时)或手动触发时,Redis 会生成当前内存中数据的完整快照(Snapshot),并将其保存为一个压缩的二进制文件(通常是 .rdb 文件)。

    • 类比(个人电脑备份):就像你定期(例如每月一次) 将电脑硬盘上所有重要的“学习资料”整体复制(备份) 到一块移动硬盘上。每次备份都是当前数据的一个完整副本。

    • 优点

      • 文件紧凑(压缩),节省磁盘空间。

      • 恢复大数据集时速度非常快(直接加载到内存)。

      • 对 Redis 主进程性能影响较小,因为备份通常在子进程进行(bgsave)。

    • 缺点

      • 会丢失最后一次快照之后的所有数据修改(取决于备份频率)。如果系统在两次备份之间崩溃,这部分数据就没了。

      • 生成快照(尤其是大数据集时)本身会消耗 CPU 和 I/O 资源,虽然主进程阻塞时间相对较短。

  2. AOF (Append Only File) - 日志追加式持久化

    • 策略:记录每一个会修改数据库状态的写操作命令(以 Redis 协议格式),并追加写入到一个日志文件(.aof 文件)中。Redis 重启时,通过重放(Replay) AOF 文件中的所有命令来重建内存数据状态。

    • 同步策略(关键补充)

      • always:每次写命令都立即同步写入磁盘。最安全,但性能最低。

      • everysec(默认):每秒同步一次。平衡了安全性和性能,最多丢失 1 秒的数据。

      • no:由操作系统决定何时同步。性能最好,但宕机时丢失数据的风险最高。

    • 类比(个人电脑备份):就像你每下载一份新的“学习资料”后,立刻(或按照一定策略,如下载完立即/每秒集中处理一次) 将这份新资料单独复制到备份移动硬盘上。备份盘记录的是所有新增文件的日志。

    • 优点

      • 数据安全性通常更高。根据 fsync 策略,最多丢失 1 秒(或更少)的数据。

      • AOF 文件是纯文本格式(Redis 协议),可读性强,便于分析和修复(redis-check-aof)。

      • 写入操作通常只追加文件末尾,写入性能较好(但同步策略影响很大)。

    • 缺点

      • 文件体积通常比 RDB 文件大得多(记录的是操作命令)。

      • 恢复大数据集时速度比 RDB 慢(需要顺序执行所有命令)。

      • 长期运行后文件会不断增大,需要定期执行 BGREWRITEAOF 来重写(Rewrite),去除冗余命令,缩小体积(类似于把多次小备份合并优化)。重写过程也会消耗资源。

关于数据安全的思考(类比扩展)

  • 单点故障风险:就像你提到的,电脑的硬盘(或备份移动硬盘)本身是最容易损坏的部件。Redis 的持久化文件也存在单个磁盘损坏的风险。

  • 解决方案

    • Redis:可以将 RDB 快照和 AOF 日志备份到不同的物理磁盘,甚至远程服务器云存储,大大降低同时丢失的风险。Redis 本身也支持同时开启 RDB 和 AOF(重启时优先使用 AOF 恢复)。

    • 个人电脑:同样,重要的“学习资料”应该遵循 “3-2-1 备份原则”:至少保留 3 份数据副本,存储在 2 种不同介质上(如电脑硬盘+移动硬盘+云盘),其中 1 份存放在异地(如另一栋楼或云端)。这能最大程度防范硬盘损坏、盗窃、火灾等灾难。

  • 持久化 ≠ 绝对不丢失:无论是 Redis 的 RDB/AOF,还是你的电脑备份,都无法保证 100% 不丢失数据。RDB 有备份间隔,AOF 的 everysec/no 策略也有风险。同样,电脑备份如果不是实时同步(如 RAID 1),也存在数据差窗口。关键在于根据数据的重要性和容忍度,选择合适的策略和备份方案

二.RDB

RDB (Redis Database) 是 Redis 的一种快照式持久化机制。其核心思想是:在特定的时间点,将 Redis 内存中存储的 所有 数据的完整状态捕获下来,生成一个紧凑的二进制快照文件(通常以 .rdb 结尾),并保存到硬盘上。

  • “拍照”比喻:就像给此刻内存中的数据拍一张“全景照片”。这张“照片”(RDB 文件)精确地记录了按下快门那一瞬间 Redis 数据库的完整内容。

  • 恢复用途:当 Redis 服务因任何原因(如重启、崩溃)导致内存数据丢失后,可以通过加载最近生成的 RDB 文件,快速地将数据恢复到拍摄快照时的状态。

  • “案发现场”比喻延伸:警察到达案发现场后,会立即封锁现场(暂停变动),并全面拍照记录现场在那一刻的所有细节和状态(生成 RDB 文件)。后续调查人员(Redis 重启进程)就能依据这些照片(RDB 文件)来精确还原现场当时的完整状况(内存数据)。

2.1.触发时机

触发 RDB 快照生成的两种主要方式:手动触发和自动触发

2.1.1.手动触发

管理员可以通过 Redis 客户端执行特定命令来主动要求生成 RDB 快照。

一般来说有下面两个命令

  1. SAVA(尽量不使用)
  2. BGSAVE(推荐)

SAVE 命令

  • 执行 SAVE 时,Redis 服务器进程会亲自、立即、全力以赴执行快照生成工作。

  • 关键缺点:阻塞性在 SAVE 命令执行期间,Redis 会暂停处理所有其他客户端的命令请求。这类似于执行 KEYS * 命令,会导致服务暂时不可用。因此,SAVE 命令在生产环境中通常应避免使用。


BGSAVE 命令 (推荐)

  • BG 代表 Background(后台)。执行 BGSAVE 时,Redis 会尝试在后台异步地进行快照生成。

  • 关键优点:非阻塞性Redis 主进程会继续正常接收并处理客户端的所有请求。快照生成工作由一个子进程来完成。

  • 实现原理 (多进程 fork)

    • Redis 是单线程处理命令请求(核心事件循环)。

    • 当执行 BGSAVE 时,Redis 主进程会调用 fork() 系统调用,创建一个与自身完全一致的子进程。这个子进程拥有主进程在 fork() 那一刻内存数据的只读副本

    • 主进程继续服务客户端。

    • 子进程基于这份内存数据的副本,将其序列化并写入硬盘,生成 RDB 文件。

    • 子进程完成写文件后退出。主进程负责收集子进程的退出状态。

    • 优势:利用操作系统的 Copy-On-Write (COW) 机制,fork() 通常非常快,且子进程操作不影响父进程。内存占用在数据修改不多时也相对可控。

    • 注意点:虽然主进程不阻塞,但 fork() 瞬间如果数据集非常大,可能会短暂停顿(与系统和内存大小有关)。生成 RDB 文件本身消耗 CPU 和 I/O 资源,对整体性能仍有潜在影响。

上面对于BGSAVE命令的解释太少了,我现在再补充一些内容:

当执行 BGSAVE 命令时,Redis 会尝试在后台异步生成 RDB 快照文件。

其内部流程如下:

1.检查并发子进程 (Conflict Check):

  • Redis 父进程首先检查当前是否已经存在正在执行的其他子进程(例如:另一个 RDB 保存进程、AOF 重写进程)。

  • Redis 通常设计为同一时间只允许一个子进程执行持久化操作(如 BGSAVE ),以避免资源(CPU、I/O、内存)竞争。这是 Redis 保证稳定性和性能的一种策略。

  • 处理逻辑: 如果检测到有活跃的子进程存在,BGSAVE 命令会立即返回一个错误信息(例如:-ERR Background save already in progress),而不会创建新的子进程。

2.父进程 Fork 创建子进程 (Fork & Blocking):

  • 确认没有冲突后,父进程调用操作系统的 fork() 系统调用创建子进程

  • 关键点: Fork 阻塞: fork() 操作本身会导致 父进程短暂阻塞。这个阻塞阻塞的时长主要取决于父进程的内存占用大小以及操作系统实现。一般来说是很短的。

  • 监控指标: 可以通过 Redis 的 INFO STATS 命令查看 latest_fork_usec 字段,该值记录了最近一次 fork() 操作消耗的微秒数 (μs),是评估 BGSAVE 或 BGREWRITEAOF 对父进程瞬时延迟影响的关键指标。一个较大的 latest_fork_usec 值(例如几百毫秒甚至秒级)意味着在 fork 期间客户端请求会经历显著的延迟。

  • 当父进程调用 fork() 成功创建子进程时:

    1. 共享物理内存 (Shared Physical Memory): 操作系统内核会为子进程创建自身的进程结构(如进程控制块 PCB),但不会立即复制父进程的内存页。子进程的页表 (Page Table) 在初始时刻指向与父进程完全相同的物理内存页。这意味着父子进程共享同一份物理内存数据

    2. 只读视图 (Read-Only View): 在 fork() 完成后的这一刻,子进程获得的是父进程内存数据在 fork() 时间点的逻辑副本快照视图。这个视图是只读的 (Read-Only)

    3. 数据一致性 (Data Consistency): 由于子进程尚未执行任何修改操作,并且它看到的是 fork() 瞬间父进程内存的状态,因此子进程此时看到的内存数据内容与父进程在 fork() 调用点的内存数据内容完全一致

    4. COW 机制保障 (COW Mechanism): 这种共享是 Copy-On-Write (写时复制) 机制的基础。后续:

      • 如果父进程修改了某块共享内存页的内容,操作系统内核会先为该页创建一个物理副本,然后父进程修改这个副本。子进程看到的仍是原始的、未修改的页。

      • 如果子进程尝试修改某块共享内存页的内容,操作系统内核同样会先为该页创建一个物理副本,然后子进程修改这个副本。父进程看到的仍是原始的、未修改的页。

      • 只有当某一方(父或子)真正尝试写入 (Write) 某个共享内存页时,该页的物理副本才会被创建。在此之前,父子进程高效地共享着所有未被修改的物理内存页。

  • 子进程就根据fork()的机制获得了只读副本,然后就由此获得了数据源

3.父进程恢复并响应 (Parent Resumes):

  • 一旦 fork() 成功完成,父进程阻塞结束

  • BGSAVE 命令向客户端返回 "Background saving started" 信息,表明后台保存任务已成功启动。

  • 关键点: 非阻塞性: 从此刻起,父进程(Redis 主服务线程)恢复运行,可以正常接收并处理所有客户端的命令请求。生成 RDB 文件的工作完全由新创建的子进程在后台独立执行,不再阻塞主服务。

4.子进程生成 RDB 文件 (Child Writes RDB):

  • 子进程拥有父进程在 fork() 时刻内存数据的只读副本(基于 COW 机制)。

  • 子进程基于这份内存数据的快照,将其序列化并写入磁盘,生成一个临时的 RDB 文件(文件名通常包含临时标记,如 temp-<pid>.rdb)。

  • 原子替换 (Atomic Replace): 当临时 RDB 文件完整且成功写入后,子进程会使用 rename 系统调用,原子性地 (atomically) 用新生成的 RDB 文件替换旧的 RDB 文件(如果存在)。这个操作在文件系统层面通常是原子的,确保了在任何时刻,磁盘上要么是完整的旧 RDB 文件,要么是完整的新 RDB 文件,避免了损坏或不完整文件被使用的情况

  • 监控指标: 可以通过 LASTSAVE 命令获取最后一次成功生成 RDB 文件的 Unix 时间戳。对应的 Redis INFO PERSISTENCE 信息中的 rdb_last_save_time 字段也记录了这个时间戳。

5.子进程通知父进程并退出 (Child Signals & Exits):

  • 完成 RDB 文件写入和替换后,子进程通过进程间通信 (IPC) 向父进程发送一个信号,通知父进程 RDB 保存操作已完成(无论成功或失败)。

  • 父进程更新统计信息: 父进程接收到子进程的信号后:

    • 更新相关的内部统计信息(如 rdb_last_save_timerdb_last_bgsave_status - 成功/失败)。

    • 记录日志(例如:Background saving terminated with success 或错误信息)。

    • 清理子进程资源(等待子进程退出,回收其状态)。

  • 子进程退出: 通知完成后,子进程随即终止退出。


2.1.2.通过配置文件自动触发

管理员可以在 Redis 配置文件 redis.conf 中预设规则,让 Redis 在满足特定条件时自动触发 BGSAVE

vim /etc/redis/redis.conf

我们找到下面这段来

我们发现没有默认是没有配置自动触发的。但是它留下了配置的规则,我们来讲解一下

将数据库状态保存至磁盘 (RDB 持久化)

Redis 提供了 save 指令配置项,用于定义何时将内存中的数据库状态异步保存到磁盘上的 RDB 快照文件(dump.rdb)。此机制是 Redis 持久化策略之一(RDB),用于在服务器故障或重启后恢复数据。

配置格式

save <seconds> <changes> [<seconds> <changes> ...]
  • <seconds>:指定的时间间隔(以秒为单位)。

  • <changes>:在该时间间隔内,数据库必须发生的最少写操作次数(增、删、改)。

触发保存的条件

只要满足配置规则中任意一条设定的条件,Redis 服务器就会启动一次后台保存操作(BGSAVE):

  1. 自上次成功保存(或 Redis 启动)以来,经过的时间达到了 <seconds> 指定的秒数。

  2. 并且,在该时间间隔内,数据库发生的写操作次数达到了或超过了 <changes> 指定的阈值。

关键点: 每个 save <seconds> <changes> 规则定义的是一个“”条件。只要其中一条规则满足(即同时达到其指定的秒数操作次数阈值),保存就会被触发。规则之间是并列关系。

禁用 RDB 快照功能

完全禁用基于 save 规则的 RDB 快照生成功能,请使用一个空字符串参数进行配置:

save ""

重要提示: 禁用后,Redis 将不再自动创建 RDB 快照文件(除非手动执行 SAVE 或 BGSAVE 命令)。这会导致服务器重启时无法通过 RDB 文件恢复数据,请确保您有其他持久化机制(如 AOF)或明确了解此风险。

默认配置

如果在配置文件 (redis.conf) 中没有显式指定任何 save 规则,Redis 将使用以下内置默认配置:

save 3600 1
save 300 100
save 60 10000

这表示:

  1. 如果距离上次保存已超过 3600 秒 (1 小时)并且在此期间至少发生了 1 次数据变更 → 触发保存。

  2. 如果距离上次保存已超过 300 秒 (5 分钟)并且在此期间至少发生了 100 次数据变更 → 触发保存。

  3. 如果距离上次保存已超过 60 秒并且在此期间至少发生了 10000 次数据变更 → 触发保存。

补充说明

  • 异步保存: 触发保存时,Redis 通常会使用 BGSAVE 命令在后台子进程中进行快照生成,这避免了阻塞主服务器进程处理客户端请求。只有在子进程创建 RDB 文件期间发生的写操作才会被包含在本次快照中。

  • 性能考量: 频繁生成 RDB 快照(尤其是间隔短、阈值低的配置)可能会对性能产生影响,我们不能频繁生成啊,因为 BGSAVE 需要 fork 子进程(在数据量大时可能耗时)并消耗 CPU 和 I/O 资源写入磁盘。

  • 数据安全性: RDB 是时间点快照。如果系统在两次快照之间崩溃,最后一次快照之后的所有数据变更将会丢失。根据数据重要性,可能需要结合 AOF 持久化或调整 save 规则以平衡性能和数据安全。

  • 手动触发: 除了自动规则,管理员还可以通过 SAVE(阻塞式)或 BGSAVE(非阻塞式)命令手动触发 RDB 快照的创建。

2.1.3.RDB 持久化的数据丢失风险场景说明

事件时间线

  1. 12:00:00

    • 此时 Redis 成功创建了一个 RDB 快照文件并写入磁盘。

    • 关键状态:此时磁盘上的 RDB 文件内容与 Redis 服务器内存中的数据完全一致

  2. 12:00:01 开始

    • Redis 服务器开始接收到大量的数据变更请求(例如执行了大量的 SETINCRDEL 等写命令)。

    • 这些操作立即更新了 Redis 内存中的数据。

    • 然而,磁盘上的 RDB 文件内容仍然停留在 12:00:00 的状态尚未更新以反映这些新变化。

  3. 12:00:20(服务器发生严重故障):Redis 服务器遭遇了不可恢复的故障,如进程崩溃、主机断电或严重硬件错误,服务器挂起。

  4. 12:01:00 (计划触发点)

    • 根据 Redis 默认的保存规则之一 save 60 10000(即 60 秒内有 10000 次变更则触发保存),在 12:01:00 这个时间点,Redis 计划启动一个新的后台进程(BGSAVE)来生成下一个 RDB 快照文件。

⚠️ 关键故障与数据丢失场景

  • 故障发生时间:在12:00:20,Redis 服务器遭遇了不可恢复的故障,如进程崩溃、主机断电或严重硬件错误。

  • 后果

    • Redis 进程终止,存储在内存中的所有数据瞬间丢失(因为内存是易失性存储)。

    • 此时磁盘上最新可用的 RDB 文件仍然是 12:00:00 生成的那个

  • 数据丢失范围

    • 12:00:00 之前的数据:安全存储在 12:00:00 的 RDB 文件中,服务器重启后可以恢复。

    • 12:00:01 至故障发生时(12:00:20)的所有数据变更:这些变更只存在于故障前一刻的内存中尚未被写入任何 RDB 文件。随着服务器崩溃,这些数据变更永久丢失,无法恢复。

  • 核心问题:即使写操作量巨大(远超 10000 次),只要自上次成功保存(12:00:00)后经过的时间未达到 60 秒< seconds> 条件未满足),Redis 就不会触发新的 RDB 保存操作。因此,两次 RDB 快照之间至少存在 60 秒(默认最小间隔)的潜在数据丢失窗口

🔍 技术要点解析

  1. RDB 的“时间点”特性:RDB 持久化通过生成某个瞬间的内存数据快照来工作。它不记录快照生成之后发生的任何写操作。

  2. 触发条件的“与”逻辑:每个 save <seconds> <changes> 规则要求必须同时满足时间间隔(<seconds> 操作次数(<changes>) 两个条件才会触发保存。在本场景中,时间间隔(60秒)是硬性要求。

  3. 内存与磁盘的延迟:写操作首先作用于高速但易失的内存,达到毫秒级响应。而将整个数据集写入较慢的持久化磁盘(RDB)则是一个相对耗时的操作(默认最短间隔为60秒)。这必然导致在两次快照之间存在一个只存在于内存中的数据“脆弱期”。

🛡️ 缓解数据丢失风险的策略

  • 调整 RDB 保存规则:可以通过配置更短的 <seconds> 值来缩小数据丢失窗口。例如,配置 save 10 1000 表示10秒内有1000次变更就保存,最大丢失窗口变为10秒。但要注意,过于频繁的 RDB 保存(尤其是数据集很大时)会消耗大量 CPU 和 I/O 资源(fork 操作和磁盘写入),影响性能。

  • 启用 AOF 持久化:AOF (Append Only File) 以日志形式记录每一条写命令。与 RDB 的“时间点”快照不同,AOF 提供了更细粒度的持久化。配合 appendfsync everysec 策略(每秒同步一次),理论上最多丢失 1 秒的数据,显著降低了数据丢失风险。但 AOF 文件通常更大,恢复速度可能较慢,且开启 AOF 会对写吞吐量有一定影响。

  • 组合使用 RDB 与 AOF (混合持久化):Redis 允许同时启用 RDB 和 AOF。这样可以利用 RDB 进行快速的全量备份和恢复,同时利用 AOF 记录增量变更以提高数据安全性。在发生故障时,通常优先使用 AOF 文件恢复(它包含更近的数据),因为它更完整。

  • 手动触发保存 (谨慎使用):管理员可以在关键操作后立即执行 SAVE 命令(阻塞所有操作直到保存完成)或 BGSAVE 命令(后台异步保存)来强制创建快照,避免等待自动规则触发。但这主要用于特定管理场景,不适合常规操作。

2.2.RDB文件的处理

生成的RDB文件默认保存在redis的工作目录里,

那么工作目录在哪里呢?我们得去redis的配置文件里面看看

vim /etc/redis/redis.conf

很显然,redis的工作目录是/var/lib/redis。这个rdb文件默认叫dump.rdb。

我们现在就去工作目录里面看看

这个dump.rdb文件就是我们上面rdb机制生成的镜像文件,redis默认是开启了rdb。

我们可以进去看看有什么东西

嗯?这里面是啥啊???其实啊,这个/var/lib/redis/dump.rdb文件存储的内容是二进制形式,我们不能修改它,这关乎到redis服务器能不能正常启动。

关于 Redis RDB 文件 (dump.rdb) 的重要特性:

  1. 二进制格式 (Binary Format):

    • /var/lib/redis/dump.rdb 文件(或其配置的路径)存储的是 Redis 内存数据的二进制快照

    • 这种格式是 高度优化和压缩 的,并非人类可读的文本格式(如 JSON, XML)。它的设计目标是高效存储快速加载,而不是直接编辑。

  2. 不可修改性 (Immutable & Critical Integrity):

    • 严禁手动修改: 您绝对不应该使用文本编辑器或其他工具尝试直接修改 dump.rdb 文件的内容。

    • 原因:

      • 结构复杂性: RDB 文件有其严格的内部二进制结构编码规则(包含魔数、版本号、数据类型、键值对、过期时间、校验和等)。任何微小的、不符合规范的改动(即使是看似无害的空格或换行)都会破坏其结构。

      • 校验机制: Redis 在加载 RDB 文件时会进行严格的格式验证和校验和(checksum)检查。如果文件被篡改或损坏,这些检查几乎必然失败

    • 后果:

      • 启动失败: 最直接的后果就是 Redis 服务器无法成功启动。在启动日志中,您很可能会看到类似 "Fatal error loading the DB: Invalid file format. Use the DUMP and RESTORE commands." 或 "Checksum mismatch" 的错误信息。

      • 数据丢失风险: 如果该文件是唯一的或主要的备份,损坏或无效的 RDB 文件意味着您可能永久丢失其包含的所有快照数据。

  3. 唯一安全的操作 (Safe Operations):

    • 备份 (Backup): 可以安全地复制整个 dump.rdb 文件到其他位置作为备份。

    • 移动 (Move): 可以通过操作系统命令(如 mv)移动文件(在 Redis 服务停止时进行)。

    • 删除 (Delete): 可以通过操作系统命令(如 rm)删除文件(通常是在确认不再需要该备份或想强制 Redis 启动一个空数据库时)。

    • 工具检查 (Inspection with Tools): 可以使用 Redis 自带的 redis-check-rdb 工具来检查 RDB 文件的完整性(例如:redis-check-rdb /var/lib/redis/dump.rdb)。注意:此工具仅用于检查,无法修复所有损坏。

  4. 数据恢复的正确途径 (Correct Way for Data Recovery):

    • 如果需要从 RDB 文件恢复数据,唯一安全可靠的方式是让 Redis 服务器进程自身在启动时自动加载它,或者使用 redis-cli 的 --rdb 选项进行导入(但这通常用于迁移)。

    • 绝对不能尝试手动解析或修改其二进制内容。

RDB 持久化机制详解

多次触发特性

RDB 持久化操作可被多次触发(通过手动命令 BGSAVE 或自动配置规则)。

临时文件写入策略

生成 RDB 快照时:

  • 数据首先写入临时文件(命名格式通常为 temp-<进程ID>.rdb)。

  • 此设计确保生成过程中原始 RDB 文件持续可用,避免服务中断。

原子文件替换

快照生成完成后:

  • 删除旧的 dump.rdb 文件

  • 通过 rename() 系统调用原子性地将临时文件重命名为 dump.rdb
    关键优势:

  • 文件系统层保证操作原子性(要么全成功,要么全失败)

  • 防止因服务器崩溃导致数据文件损坏部分写入

单文件一致性原则

  1. 始终只存在一个有效的 dump.rdb 文件

  2. 旧文件在新文件完全就绪后才被替换

  3. 服务重启时仅加载唯一有效快照,避免数据错乱

2.3.RDB效果演示

2.3.1.手动执行bgsave(自动触发关闭的时候)

我们打开客户端,先清除所有键值对

 我们进去看看这个rdb文件

vim /var/lib/redis/dump.rdb

接下来我们回到我们的客户端,执行一些命令

接下来我们回到这个rdb文件

vim /var/lib/redis/dump.rdb

嗯?好像没有什么变化啊!!

这个是因为rdb文件里面的内容,不是你这步插入了数据,就会立即更新的。

rbd的触发时机

  1. 手动触发:save,bgsave
  2. 自动触发(配置文件中设置)

刚才插入几个键值对,没有执行手动触发的命令,也达不到自动触发的条件,所以没有变化。

我们去看看自动触发的条件,这需要去配置文件看看

vim /etc/redis/redis.conf

我们发现是没有进行配置的,所以目前我这个机器是不会自动触发的。

这样子我们还是进行手动执行save或者bgsave触发一次生成快照

我们这里数据比较少,执行bgsave瞬间就完成了,立即查看应该就是有结果的。如果后面我们接触的数据多了,执行bgsave就可能需要消耗一定的时间,我们立即查看不一定是生成完毕的了

跟上面的有变化了吧。

接下来我们重启redis服务器

service redis-server restart


事实上在redis服务器重启的过程中,经历了下面这些过程

服务器停止时的数据状态

  1. 内存数据丢失
    当 Redis 服务器进程停止运行(正常关闭或崩溃)时:

    • 存储在 内存(RAM)中的所有数据立即消失(内存的易失性特性)。

    • 此时服务器不再持有任何有效数据

  2. 磁盘数据保留
    磁盘上的持久化文件(如 RDB 或 AOF)不受进程停止影响,仍保留在配置的存储路径中(例如 /var/lib/redis/)。

重启时的数据恢复流程

  1. 自动检测持久化文件
    Redis 启动时,会自动检查配置的持久化目录(默认 /var/lib/redis/)。

    • 优先查找 AOF 文件appendonly.aof),若存在则加载 AOF。

    • 若未启用 AOF,则查找 RDB 文件(默认 dump.rdb)。

  2. 从 RDB 文件恢复数据(典型场景)
    当检测到 dump.rdb 文件存在时:

    • Redis 会将 RDB 文件加载到内存中。

    • 恢复逻辑:RDB 是一个压缩的二进制快照,Redis 解析文件内容,按快照生成时的状态重建整个数据库(包括所有键值对、过期时间等元数据)。

    • 恢复完成标志:数据库状态回退到最后一次成功生成 RDB 的时间点

  3. 恢复后状态验证

    • 执行 INFO persistence 命令可查看 rdb_last_load_time(上次加载 RDB 的时间戳)。

    • 所有客户端可见的数据 = RDB 文件保存的内容(不含快照后的更新)。

 重启之后我们去看看我们还能不能获取到key,key2,key3

很显然可以获得。

2.3.2.插入新的key,不手动执行bgsave

我们在上面那个的基础之上再去插入一个新的key4

这个时候我们直接重启redis服务器

service redis-server restart

这个时候我们重新登陆redis服务器去看看

嗯?key4怎么还存在啊?

事实上redis生成快照操作,不仅仅是手动执行命令才会触发,也可以自动触发。

我们需要了解一下自动触发的情景

Redis RDB 快照的自动触发机制

Redis 生成 RDB 快照(持久化到磁盘)的操作不仅可以通过手动执行 SAVE 或 BGSAVE 命令触发,还内置了多种自动触发场景,确保数据在特定条件下得以保存:

  1. 基于配置规则的自动触发 (save):

    • 在 Redis 配置文件 redis.conf 中,可以通过 save <seconds> <changes> 指令设置触发条件。

    • 机制: 当在指定的 M 秒时间窗口内,数据库发生了至少 N 次修改(增、删、改)时,Redis 会自动在后台启动一个 BGSAVE 操作来生成新的 RDB 快照文件。

    • 示例: save 900 1 表示如果 900 秒(15分钟)内有至少 1 个 key 发生变化,则触发 BGSAVEsave 300 10 表示 300 秒(5分钟)内有至少 10 次变化则触发。

  2. 正常关闭服务器触发:

    • 当使用 Redis 内置的 SHUTDOWN 命令,或者通过系统服务管理命令(如 service redis-server stop / systemctl stop redis正常关闭 Redis 服务器时,Redis 会作为关闭流程的一部分,自动尝试执行一次持久化操作

    • 机制: 默认情况下,SHUTDOWN 会触发一个 SAVE 操作(阻塞式,直到完成),如果配置允许且时间充足,它也可能尝试非阻塞的 BGSAVE(但会等待其完成)。这确保了服务器在优雅退出前将最新数据保存到 RDB 文件。通过 service redis-server restart 这类命令进行的“重启”,其本质是先正常关闭(触发保存)再启动。

  3. 主从复制全量同步触发:

    • 当一个新的从节点 (replica) 首次连接到主节点 (master) 并请求进行全量同步(而不是部分同步)时,主节点需要将自己的完整数据集发送给从节点。

    • 机制: 为了完成这个操作,主节点会自动在后台启动一个 BGSAVE 进程,将当前内存数据生成一个 RDB 快照文件。

    • 后续流程: 生成完成后,主节点将这个 RDB 文件传输给从节点。从节点接收并加载这个 RDB 文件,从而获得主节点数据的完整副本。之后,主节点会将生成 RDB 文件期间以及传输过程中产生的新的写命令,通过复制缓冲区发送给从节点,最终达到数据一致的状态。

注意:只有正常关闭服务器才会触发

当在 Redis 中插入新的 Key(例如 key4)后,如果没有手动执行 BGSAVE 命令,数据是否会丢失取决于服务器如何重启:

  1. 正常流程重启:

    • 如果通过 shutdown 命令或类似 service redis-server restart 的管理命令正常关闭 Redis 服务器,Redis 在退出前会自动触发一次 BGSAVE 操作,将内存中的数据生成快照(RDB 文件)保存到磁盘。

    • 结果: 即使之前没有手动执行 BGSAVE,重启后服务器加载这个最新的 RDB 文件,所有数据(包括新插入的 key4)都会恢复。

  2. 异常重启:

    • 如果 Redis 服务器因进程被强制终止(kill -9)或服务器突然掉电而异常关闭,它没有机会执行正常的退出流程。

    • 结果: 自上次成功生成 RDB 快照以来,所有在内存中尚未被持久化的修改(包括新插入的 key4)将会丢失。重启后 Redis 只能加载上一次成功的 RDB 快照或 AOF 文件(如果启用且配置合理)中的数据。

我们可以看看使用kill命令触发的服务器停止的情况

我们发现key6不见了,这就是异常重启的情况。

2.3.3.bgsave / save的执行过程

Redis 的 BGSAVE 命令操作流程是:主进程会创建一个子进程,由这个子进程负责完成将内存数据持久化到磁盘的工作。

关键点在于,子进程进行持久化时,会将数据写入一个全新的临时 RDB 文件中。只有当这个新文件被完整且成功地写入磁盘后,子进程才会用它原子性地替换掉旧的 RDB 文件(通常是 dump.rdb)。这种“写时复制”和“原子替换”的机制确保了即使在持久化过程中发生故障,旧的备份文件也不会被破坏。

不过,如果数据集非常小或者服务器性能极强BGSAVE 操作可能会完成得非常快。在这种情况下,由于子进程的生命周期极短,通过系统工具(如 ps)可能难以清晰地观察到子进程的存在。

我们来看看

  • inode 是文件的唯一标识:新文件必然拥有全新的 inode 编号

我们先来看看rdb文件的innode编号

这个时候,我们去客户端执行bgsave

这个时候我们再去使用stat命令看看

发现了吧inode编号发生了变化。

save

事实上,不只是bgsave,其实save也是一样的

Redis 在执行持久化操作(包括 SAVE 和 BGSAVE)时,永远不会直接修改原始 RDB 文件。而是采用以下安全流程:

  1. 创建临时文件:Redis 会生成一个新临时文件(如 temp-<进程ID>.rdb

  2. 写入数据:将所有内存数据写入这个全新的临时文件

  3. 原子替换:使用 fsync() 确保数据落盘后,通过 rename() 系统调用将临时文件原子性重命名为 dump.rdb

我们看看

我们发现inode变化了,这说明save也会生成一个新文件去替换掉之前的rdb文件

2.3.4.通过配置自动生成rdb快照

注意:flushall会清空rdb文件。

接下来我们打开配置文件

vim /etc/redis/redis.conf

大家找到下面这个

直接添加

这个save 60 2的意思:如果距离上次保存已超过 60 秒 并且在此期间至少发生了 2 次数据变更 → 触发保存。

这个时候,我们需要重启redis服务器,这个重启是为了让我们的配置生效

service redis-server restart

这个时候我们回去redis客户端看看

注意:我们60秒后重启redis服务器看看(注意得使用kill -9)

我们回去redis客户端看看

保存下来了。!!!

2.3.5.如果把rdb文件故意改坏了,会怎么样?

接下来

vim /var/lib/redis/dump.rdb

本来是下面这样子

我们自己在最后面随便加一点东西进去

保存退出。

这个时候我们需要重新启动一下redis服务器(注意只能使用kill -9

注意:
一定是通过 kill 进程的方式, 重新启动 redis 服务器.
如果通过 service redis-server restart 重启, 就会在 redis 服务器退出的时候, 重新生成 rdb 快照.
就把咱们刚才改坏了的文件给替换掉了~~

我们回去看看

vim /var/lib/redis/dump.rdb

没有什么变化。我们回去redis客户端看看

看起来没有什么变化啊!

刚才是改坏了, 但是看起来好像 redis 服务器没有收到啥影响~~ 还是能正常启动, 还是能正确获取到 key ~~

这里具体 redis 会咋样, 取决于 rdb 文件坏了的地方在哪~~

刚才是改坏的地方正好是文件末尾, 对前面的内容没啥影响.

如果是中间位置坏了, 可就不一定了~~

将中间位置的内容进行修改

我们来看看

vim /var/lib/redis/dump.rdb

我们把中间的地方进行修改

保存退出,然后重启我们的redis服务器。

当把中间位置改坏了之后, 发现, redis 服务器, 启动不了了!!

我们使用下面这个命令来重启一下redis看看

service redis-server restart

还是启动不了。

当 redis 服务器挂了的时候, 可以看看 redis 日志, 了解一下发生了啥~~

tail -n10 /var/log/redis/redis-server.log

日志说出现了错误。

rdp 文件是二进制的~~  

直接就把坏了的 rdp 文件交给 redis 服务器去使用,  
得到的结果是不可预期的~~  

可能 redis 服务器能启动, 但是得到的数据可能正确也可能有问题;  
也可能 redis 服务器直接启动失败.....  

redis 也提供了 rdp 文件的检查工具~~  
可以先通过检查工具, 检查一下 rdp 文件格式是否符合要求~~  

ls -l /usr/bin/redis-*

检查工具, 和 redis 服务器,  在 5.0 版本是同一个可执行程序.  

我们可以在运行的时候加入不同的选项,  从而使用其中的不同功能~~  

运行的时候, 加入 rdp 文件作为命令行参数  

此时就是以检查工具的方式来运行, 不会真的启动 redis 服务器~~

redis-check-rdb /var/lib/redis/dump.rdb

这里面就指出了一些错误。

这个 Redis RDB 文件检查报告显示您的 dump.rdb 文件存在严重结构损坏。让我们逐行分析错误报告并解释其含义:

📊 RDB 文件检查结果深度解析

[offset 0] Checking RDB file /var/lib/redis/dump.rdb
  • 检查开始,验证文件完整性

[offset 27] AUX FIELD redis-ver = '7.0.15'
  • ✅ 文件头验证通过:Redis 版本为 7.0.15(正常)

[offset 41] AUX FIELD redis-bits = '-62'
  • ⚠️ 首个异常信号redis-bits 应为正数(通常 32/64),负值表示元数据损坏

--- RDB ERROR DETECTED ---
[offset 42] Invalid object type: 128
  • ❌ 致命错误:在文件偏移量 42 字节处发现非法数据类型标识符 128

  • 合法类型范围:0-9(如 0=字符串, 1=列表等),128 完全越界

[additional info] While doing: read-type
[additional info] Reading type 0 (string)
  • 🔍 错误上下文:Redis 试图读取一个字符串类型(type 0)的数据时发生错误

  • 说明损坏发生在第一个键值对解析过程中

[info] 0 keys read
[info] 0 expires
[info] 0 already expired
  • 💥 最终结果:0 个键值对加载成功,文件完全不可用

这个大家看看即可。

2.4.RDB的优缺点

优点:

  • 紧凑高效的快照: RDB 文件是一个经过高度压缩的二进制文件,它代表了 Redis 数据库在某个特定时间点的完整数据状态。这种格式使其成为备份、全量复制和灾难恢复的理想选择。例如,可以配置每 6 小时执行一次 bgsave 命令进行备份,并将生成的 RDB 文件传输到远程服务器或分布式文件系统(如 HDFS)进行安全存储。

  • 极速的数据恢复: Redis 使用 RDB 文件恢复数据的速度远快于 AOF 方式。这是因为 RDB 文件直接以二进制格式存储了内存数据的快照,加载时只需将文件内容高效地读入内存并重建数据结构即可,避免了复杂的解析过程。

  • 高效的存储格式: RDB 使用紧凑的二进制格式组织数据,存储效率高。加载时,Redis 可以直接将二进制数据按字节读取并映射到内存中的数据结构(结构体/对象),处理速度非常快。

缺点:

  • 非实时持久化: RDB 最大的局限性在于无法提供实时或秒级的数据持久化保证。生成 RDB 快照(通常通过 bgsave 命令)是一个相对重量级的操作:

    • 它需要 fork 出一个子进程 来执行实际的磁盘写入工作。

    • fork 操作本身(尤其是在数据量大的情况下)可能消耗较多 CPU 时间和内存(涉及写时复制机制)。

    • 因此,出于性能考虑,不适合频繁执行(例如每秒一次)。这意味着在两次快照之间,如果发生故障,最近一次快照之后的所有数据更新将会丢失

  • 潜在的版本兼容性问题:

    • RDB 文件使用特定的二进制格式。随着 Redis 版本的演进,RDB 的文件格式也可能发生变化(存在多个 RDB 版本号)。

    • 将旧版本 Redis 生成的 RDB 文件加载到新版本 Redis 中通常可以兼容,但反之(新版本 RDB 加载到旧版本)则可能失败或存在兼容性风险

    • 应对策略:

      • 在大多数生产环境中,通常会保持 Redis 服务器集群使用统一的版本,以最小化此类风险。

      • 如果确实需要进行跨不兼容版本的迁移(例如升级 Redis 主版本),简单的 RDB 文件复制加载可能行不通。此时,更可靠的方法是编写迁移程序或使用工具:连接到旧 Redis 实例,遍历所有键(KEYS * 或使用 SCAN 命令),读取数据,然后将其写入新版本的 Redis 实例中。这种方式绕过了 RDB 格式的依赖,确保了数据的正确转移。

总结核心痛点:

        RDB 持久化最主要的缺点是其非实时性。在两次成功生成 RDB 快照的间隔期内发生的所有数据修改,若遇到服务器宕机等故障,将无法恢复,造成数据丢失。因此,RDB 更适合对数据完整性要求允许少量丢失(由备份频率决定)、但对恢复速度要求极高的场景。对于需要更高持久性保证(最小化数据丢失)的场景,应结合使用 AOF 或采用 RDB+AOF 的混合模式。

三. AOF

3.1.什么是AOF

我们来深入探讨一下 Redis 的 AOF(Append Only File) 持久化机制。它是 Redis 除了 RDB 之外,另一种非常重要的持久化方式,核心目标是解决 RDB 在实时性/数据安全性上的不足

核心概念:

  1. 记录操作,而非状态: 与 RDB 记录某个时刻的完整数据快照不同,AOF 记录的是导致数据状态发生变化的写命令(例如 SETHSETSADDLPUSH 等)。

  2. 只追加日志: 正如其名 Append Only File,AOF 的工作方式是将这些写命令以 Redis 协议格式追加写入到一个文本文件的末尾。这是一个顺序写操作,通常比 RDB 生成快照(涉及内存遍历和压缩)效率更高。

  3. 重建数据: 当 Redis 重启需要恢复数据时,它会重新执行(Replay) AOF 文件中记录的所有写命令,从而在内存中重建出数据库关闭前的状态。

AOF 的核心工作流程:

  1. 命令执行: 客户端发送一个写命令给 Redis。

  2. 写入内存: 命令在 Redis 内存中执行,数据被修改。

  3. 追加到 AOF 缓冲区: 命令执行完成后,其协议内容会被追加到内存中的 AOF 缓冲区。这一步非常快。

  4. 写入磁盘: 根据配置的 appendfsync 策略,Redis 会以不同的频率和方式将 AOF 缓冲区的内容同步(fsync) 到磁盘上的 AOF 文件中。这是保证持久化的关键步骤,也是影响性能的主要因素。

AOF 的优缺点:

  • 优点:

    • 更高的数据安全性: 通过合理配置 appendfsync (尤其是 everysec 或 always),可以大大降低数据丢失的风险,达到近似实时持久化的效果。这是 AOF 相对于 RDB 最核心的优势

    • 易于理解和解析: AOF 文件是纯文本格式(Redis 协议),格式相对简单,便于人工阅读(虽然通常不这么做)和编写工具处理。即使文件损坏,也比较容易定位和修复(使用 redis-check-aof 工具)。

    • 追加写性能较好: 顺序追加写入操作通常比 RDB 生成快照更快(尤其是在 appendfsync 为 everysec 或 no 时)。

    • 容错性: 默认情况下,如果 AOF 文件在写入过程中被截断(比如磁盘满了),Redis 在启动时检测到这个问题,会尝试加载文件中完整的命令部分,而不是完全失败。redis-check-aof 工具也可以帮助修复。

  • 缺点:

    • 文件体积通常比 RDB 大: 记录了所有写操作历史,即使经过重写,相同数据集下 AOF 文件通常仍比 RDB 文件大。

    • 数据恢复速度相对较慢: 需要重新执行所有命令来重建数据,恢复速度慢于直接加载 RDB 快照,尤其是在数据集很大时。

    • 性能受 fsync 策略影响大: 如果配置为 always,性能开销巨大。配置为 everysec 时,在极端情况下(如磁盘 IO 饱和)也可能遇到短暂的性能波动。BGREWRITEAOF 和 fsync 一样,在数据量大时 fork 操作也可能有性能开销。

    • 历史命令的潜在问题: 如果系统在记录了一个错误的命令(比如逻辑上有 Bug 的命令)之后崩溃,重启后重放 AOF 可能会再次引入这个错误(RDB 快照则不会,因为它记录的是最终状态)。虽然这种情况较少见。

3.3.使用AOF

当开启AOF时,rdb就不生效了,Redis 重启后恢复数据会优先使用 AOF 文件.

aof默认是不开启的,我们需要去配置文件里面配置开启一下才行。

首先我们打开配置文件

vim /etc/redis/redis.conf

大家找到下面这个

把这个No改成yes,就开启了AOF

我们再往下翻

这个appendfilename就是AOF文件的默认文件名。

至于这个AOF文件所在的也是redis的工作目录。

注意了:

        在 Redis 7.0+ 的 Multi Part AOF (MP-AOF) 架构中,混合持久化(RDB preamble)是 AOF 重写 (BGREWRITEAOF) 的默认行为。它的确提供了更快的重启加载速度,但也带来了 RDB 二进制格式的特性。

        如果你明确希望只使用纯文本命令格式的 AOF,完全禁用混合持久化(即禁用重写时生成 RDB 格式的 Base 文件),除了上面那个之外,我们还需要配置一个选项:

把yes给修改成no

我们保存退出,重新启动redis

service redis-server restart

这个时候我们可以去redis工作目录下面看看

ls -l /var/lib/redis/

这个/var/lib/redis/appendonlydir里面的文件就是我们的AOF的文件。

AOF文件介绍

1. appendonly.aof.manifest(清单文件)

  • 核心作用:这是整个 AOF 系统的总控文件,相当于文件系统的目录索引。

  • 功能细节

    • 记录当前生效的 AOF 文件序列(Base 和 Incr 文件的组合)

    • 定义文件加载顺序:先加载 Base 文件重建基础状态,再按顺序应用 Incr 文件中的增量命令

    • 管理文件版本号(如你看到的 2),每次 AOF 重写会生成新一代文件序列

  • 重要性:Redis 重启时首先读取此文件,根据其指示加载对应文件。没有它,Redis 无法识别其他 AOF 文件。

2. appendonly.aof.2.base.aof(基础文件)

  • 核心作用:存储数据库在执行 AOF 重写时刻的完整状态

  • 关键特性

    • 由 BGREWRITEAOF 命令触发生成

    • 内容是通过扫描内存数据生成的最小命令集合(如 SETHSET 等重建数据所需的命令)

    • 文件后缀 .aof 表示这是纯文本格式(因你配置了 aof-use-rdb-preamble no

  • 特殊状态说明
    你看到的 0 字节表示触发重写时数据库为空。正常有数据时,此文件会包含重建数据库所需的全部命令。

3. appendonly.aof.2.incr.aof(增量文件)

  • 核心作用:记录基础文件生成后发生的所有新写操作

  • 工作逻辑

    1. 当 Redis 执行写命令(如 SETINCR)时

    2. 命令会先写入内存缓冲区

    3. 根据 appendfsync 配置(如每秒同步)

    4. 最终追加到此增量文件的末尾

  • 特殊状态说明
    0 字节表示自上次 AOF 重写后尚未有新数据写入。一旦有新操作,文件大小会立即增长。

appendonly.aof.2.base.aof和appendonly.aof.2.incr.aof的关系

当我们执行 BGREWRITEAOF 命令时,Redis 会在后台启动一个子进程执行 AOF 重写操作。该操作会创建一个新的基础文件,例如 appendonly.aof.2.base.aof

  • 基础文件 (base.aof):这个文件本质上类似于 RDB 文件,它包含了触发重写那一刻 Redis 内存中所有数据的一个快照(snapshot)。生成完成后,这个文件的内容就固定了,Redis 不会再向其中追加任何新的写操作命令。

  • 增量文件 (incr.aof):在 BGREWRITEAOF 执行期间以及后续运行中,只要新的 AOF 重写条件尚未满足,所有接收到的写操作命令都会被追加记录到对应的增量文件中,例如 appendonly.aof.2.incr.aof

因此,在两次 AOF 重写之间产生的所有数据变更,都会持续写入到当前活跃的增量文件 (incr.aof) 中,而基础文件 (base.aof) 则保持为创建时的快照状态。

看看AOF文件里面的内容

我们这个时候把dump.rdb文件删除

现在我们重启redis

service redis-server restart

这个时候我们回去看看AOF文件

appendonly.aof.2.base.aof(基础文件)里面还是啥都没有

appendonly.aof.2.incr.aof(增量文件)多了一些东西

这个时候,我们重启redis服务器

我们发现之前的键值对都还在

我们回redis工作目录看看

接着我们去看看

vim /var/lib/redis/appendonlydir/appendonly.aof.2.incr.aof

vim /var/lib/redis/appendonlydir/appendonly.aof.2.base.aof

然后我们记住它们的inode

接下来我们打开客户端,去执行下面这些命令

我们重新去看看inode

嗯?文件不存在?接着我们回去看看aof文件

我们发现是序号变化了啊。

我们看看inode

我们发现和之前的inode是完全不一样了啊。说明不是重命名而是生成了新文件。

我们进去看看

vim /var/lib/redis/appendonlydir/appendonly.aof.3.incr.aof

vim /var/lib/redis/appendonlydir/appendonly.aof.3.base.aof

我们发现序号为3的incr文件里面没有东西了,但是我们仔细观察的话之前序号为2的incr文件里面的所有内容全部都转移到现在序号为3的base文件里面了。

这个时候我们重新去客户端执行一些命令看看

这个时候我们再回去看看

vim /var/lib/redis/appendonlydir/appendonly.aof.3.incr.aof

vim /var/lib/redis/appendonlydir/appendonly.aof.3.base.aof

我们发现base文件没有修改,但是incr文件多了一些东西。

这很符合我们的预期啊!!!!

3.4.AOF为什么快?

Redis 虽然是单线程服务器,但速度非常快。

核心原因在于其操作主要基于内存。 内存的访问速度远高于磁盘。

引入 AOF 持久化后(写内存 + 写硬盘),是否会影响 Redis 的处理速度?

实际上,AOF 的设计巧妙地将对主线程性能的影响降到了最低。 关键在于两点:

  1. 缓冲区机制(Buffering):

    • Redis 工作线程(主线程) 处理完写命令后,并不会直接将命令写入硬盘。

    • 而是先将命令追加到内存中的一个缓冲区(AOF Buffer) 里。

    • 这样,主线程只需进行快速的内存写操作,就能立即返回处理下一个请求,不会被慢速的磁盘 I/O 阻塞

  2. 顺序追加写入(Sequential Appending):

    • 当需要将缓冲区内容持久化时,AOF 采用追加(Append) 的方式写入到现有 AOF 文件的末尾。

    • 硬盘对于顺序写入(Sequential Write) 的性能远高于随机写入(Random Write)(虽然顺序写仍比内存慢很多)。

    • 通过缓冲区积累一批命令后一次性顺序写入磁盘,显著减少了实际的磁盘写入次数(I/O 操作次数),这是提升效率的关键。

    • 举例: 处理 100 个写请求。AOF 机制可能将这 100 个请求对应的命令先在内存缓冲区累积,然后一次性顺序写入硬盘。这比每个请求都直接触发一次磁盘写入(分 100 次写)要快得多!

3.5.AOF的刷屏策略

缓冲区的数据安全风险:

  • 数据写入缓冲区后,本质仍在内存中

  • 如果此时发生进程崩溃或服务器掉电,缓冲区中尚未写入硬盘的数据就会丢失

性能与可靠性的权衡(Trade-off):

  • Redis 提供了 appendfsync 配置选项,让用户根据实际需求在性能数据可靠性之间做出选择。这类似于关系型数据库(如 MySQL)的事务隔离级别设置,都是在不同维度上做取舍。

  • 这个选项控制着缓冲区内容同步(fsync)到硬盘的策略和频率

    • 刷新 (fsync) 频率越高: 数据可靠性越高(丢失风险越低),但对性能的影响越大(因为频繁等待磁盘 I/O 完成)。

    • 刷新 (fsync) 频率越低: 对性能影响越小(吞吐量越高),但数据可靠性越低(丢失风险越高)。

appendfsync 策略(关键配置项):

这个配置决定了 AOF 缓冲区的内容何时被真正写入磁盘,直接影响了数据安全性、性能和 Redis 的行为:

  • always

    • 原理: 每个写命令执行完成后,立即同步将缓冲区数据写入磁盘文件(调用 fsync)。

    • 优点: 数据安全性最高。即使服务器宕机,最多只丢失一个命令的数据。

    • 缺点: 性能开销最大。频繁的磁盘 fsync 操作会严重拖慢 Redis 的吞吐量。通常只在对数据一致性要求极其严苛的场景使用。

  • everysec (默认值):

    • 原理: 写命令先放入缓冲区。Redis 使用一个后台线程每秒执行一次缓冲区数据写入磁盘文件(调用 fsync)。

    • 优点: 在数据安全性和性能之间取得了较好的平衡。理论上,宕机最多丢失最近 1 秒内的写命令。

    • 缺点: 如果系统在两次同步之间(不足 1 秒)崩溃,仍会丢失这不到 1 秒的数据。如果磁盘负载过高导致 fsync 阻塞超过 1 秒,可能会影响后续命令的处理(虽然 Redis 会尽量让主线程继续处理命令,但缓冲区可能堆积)。

  • no

    • 原理: 写命令只放入缓冲区。由操作系统决定何时将缓冲区数据冲刷(flush) 到磁盘文件(通常是在缓冲区满了或者操作系统自己的策略触发时)。

    • 优点: 性能最好,因为 Redis 完全不主动执行 fsync

    • 缺点: 数据安全性最低。宕机时可能丢失大量数据(取决于操作系统刷盘的频率,通常是 30 秒左右)。不推荐在生产环境中使用

我们来找找看:

vim /etc/redis/redis.conf

我们可以在vim的命令行模式里面写入/appendfsync,就能快速找到

可以看到,默认就是everysec。

3.6.AOF重写机制

AOF机制存在的问题

未经重写的原始 AOF 文件体积会不断增长,主要原因在于其记录方式:

  1. 记录过期数据: 即使某些键的数据在内存中已经过期(TTL到期),但在它们过期之前发生的相关写命令(如 SET)仍然会保留在 AOF 文件中。Redis 在写入命令时并不知道该数据未来何时会过期。这些命令在数据过期后变得完全无效且冗余,却持续占用文件空间。

  2. 保留所有中间命令和无效操作: AOF 忠实记录每一条收到的写命令及其历史轨迹。这导致:

    • 冗余命令: 对同一个键的多次修改(如反复 SET 同一个键的值)都会按顺序记录,即使只有最后一次 SET 是有效的。之前的 SET 命令在后续命令执行后就变得无效。我举一个例子,就好像我让小明的分数+10,后面又-8,后面又减去9,这样子AOF就会记录3条记录,但事实上我们自己很清楚,这3条记录只需要一条记录即可完成:让小明的分数-7即可达到最终目的

    • 无效命令: 显式删除数据的命令(如 DEL keyHDEL fieldSREM member)会被记录。虽然这些命令执行后删除了数据,但它们本身作为历史操作依然存在于文件中,增加了文件大小。

    • 中间状态: 文件包含了数据达到最终状态过程中所有的中间修改步骤,而这些中间状态在恢复时是不必要的。

  3. 分散记录操作,缺乏合并: 对于需要多个步骤构建的数据结构,AOF 会按操作发生的顺序逐一记录每一条命令:

    • 例如,向一个列表(List)依次添加三个元素 abc,会生成三条独立的命令 LPUSH list aLPUSH list bLPUSH list c(或 RPUSH),每条命令都占据一定的空间。

    • 向一个集合(Set)逐个添加三个成员,会生成三条 SADD set memX 命令。

    • 这种分散记录的方式相比直接记录最终状态(如一条 LPUSH list a b c 或一条 SADD set mem1 mem2 mem3)会显著增加命令数量和文件体积。每条命令都有额外的开销(命令名、参数分隔符、换行符等)。

总结导致文件变大的核心:

原始 AOF 文件就像一个只追加、不清理的操作日志。它不加甄别地记录所有写操作的历史,包括:

  • 最终会过期的数据相关的命令。

  • 被后续命令覆盖或删除的数据相关的命令(冗余和无效命令)。

  • 构建数据结构过程中产生的、可以被合并的大量零散操作命令。

这种积累历史而非仅记录终态的特性,是 AOF 文件在没有重写干预的情况下体积持续增长的根本原因。AOF 重写机制正是为了解决这些问题而设计的。

AOF重写机制

        随着命令不断写入 AOF(Append Only File)文件,其体积会持续增长。为了解决文件膨胀问题,Redis 引入了 AOF 重写机制。该机制的核心是将 Redis 进程当前内存中的数据状态,转化为重建这些数据所需的最精简写命令序列,并同步写入一个全新的 AOF 文件,从而显著压缩文件体积。

重写后 AOF 文件变小的原因:

  1. 忽略过期数据: 进程内已经超时的数据不会被写入新的 AOF 文件。

  2. 删除无效命令: 旧 AOF 文件中记录数据中间状态的冗余命令(例如多次修改同一个键产生的 SETDELHDELSREM 等命令)会被移除。重写后的文件只包含能生成数据最终版本的必要命令。

  3. 合并写操作: 对于同一个键的多个连续写操作,尤其是列表(List)、集合(Set)、哈希(Hash)、有序集合(Sorted Set)等类型,会被尽可能地合并为更少的命令。例如,旧文件中的 LPUSH list aLPUSH list bLPUSH list c 三条命令,在重写后可能被合并为一条 LPUSH list a b c 命令。对于集合类型,重写会直接生成创建完整数据结构的命令(如 SADD set mem1 mem2 mem3),而不是记录每个元素的单独添加操作。

AOF 重写的好处:

  • 降低硬盘空间占用: 移除了过期数据、冗余命令和中间状态,文件体积大幅减小。

  • 提升数据恢复速度: Redis 重启时加载并执行 AOF 文件中的命令来恢复数据。文件体积越小、命令越精简,恢复过程就越快。

AOF 重写的触发方式:

  1. 手动触发: 通过执行 BGREWRITEAOF 命令显式发起重写。Redis 会启动一个后台子进程来执行重写任务,主进程可以继续处理客户端请求,避免阻塞服务。

  2. 自动触发: Redis 根据配置参数自动判断何时触发重写。触发需要同时满足以下两个条件:

    • auto-aof-rewrite-min-size 设置触发重写时 AOF 文件大小的最小阈值(默认值为 64MB)。只有当 AOF 文件的当前大小超过此阈值时,才可能触发重写。这避免了文件过小时就进行不必要的重写操作。

    • auto-aof-rewrite-percentage 设置 AOF 文件当前大小相对于上一次成功重写后的大小增长百分比阈值(默认值为 100%,即增长一倍)。例如,如果上次重写完成后文件大小为 40MB,且百分比阈值设为 100%,那么当文件增长到 80MB 或更大时,结合最小阈值条件,就会触发自动重写。

重写过程的关键细节:

  • 后台子进程与数据快照: 当重写启动(无论是手动还是自动),Redis 主进程会 fork 出一个子进程。这个子进程拥有主进程在 fork 时刻的内存数据副本(快照)。子进程基于这份内存快照数据来生成新的、精简的 AOF 文件内容,而不是去解析旧的、臃肿的 AOF 文件。

  • 双缓冲区保证数据完整性: 在子进程进行重写期间,主进程仍在持续接收并处理新的客户端写命令。这些新命令会:

    • 按正常的 AOF 持久化策略(由 appendfsync 配置)写入原有的 AOF 缓冲区,最终同步到旧的 AOF 文件中。

    • 同时被写入一个专门的 AOF 重写缓冲区。这个缓冲区确保了重写过程中发生的数据变更不会丢失。

  • 原子切换新文件: 当子进程完成基于快照的新 AOF 文件写入后,它会通知主进程。主进程会:

    1. 将 AOF 重写缓冲区 中积累的所有命令(即重写期间发生的数据变更)追加到子进程创建的新 AOF 文件末尾。

    2. 对新的 AOF 文件进行 fsync 以确保数据落盘(根据配置策略)。

    3. 原子操作(atomic rename) 的方式,用这个包含了完整最新数据的新 AOF 文件替换掉旧的 AOF 文件。此后,所有的写命令将开始追加到这个新文件。

  • 潜在的性能考量:

    • fork 操作在内存数据量很大时可能耗时较长且占用较多内存(涉及复制页表)。

    • 子进程写入新 AOF 文件和最后主进程 fsync 新文件时会产生磁盘 I/O 压力。可以通过配置 no-appendfsync-on-rewrite yes 来减轻影响(该选项开启时,在重写期间主进程对旧 AOF 文件的 fsync 可能会被推迟,牺牲一点旧文件的持久性保证来换取性能)。

AOF重写过程

1. 触发重写请求

  • 客户端手动或自动策略触发BGREWRITEAOF命令,触发重写请求。

  • 执行条件检查

    • 若当前进程正在执行AOF重写,则拒绝新请求。

    • 若当前进程正在执行BGSAVE(RDB持久化),则延迟重写至BGSAVE完成后执行。

2. 父进程创建子进程(fork)

  • 父进程调用fork()系统调用创建子进程。

  • 关键特性

    • 子进程获得父进程fork时刻的内存数据副本(写时复制机制)。

    • 此副本即为后续重写的数据源不依赖旧AOF文件内容。

3. 父子进程并行工作

父进程(主线程)

  1. 继续响应客户端请求,处理新命令。

  2. 新命令的AOF数据按正常流程写入:

    • 先追加至AOF缓冲区aof_buf

    • 根据appendfsync策略(always/everysec/no)同步到原始的AOF文件

  3. 额外写入重写缓冲区

    • 所有fork后接收的新命令同时写入aof_rewrite_buf缓冲区(专属重写的临时内存区)。

子进程

  1. 基于内存快照生成新AOF文件

    • fork时复制的内存数据,按AOF格式转化为重建数据的最简命令集

    • 写入全新的临时AOF文件(如temp-rewriteaof-bgpid.aof)。

  2. 优化策略

    • 忽略过期数据,删除无效命令(如被覆盖的SET、已执行的DEL)。

    • 合并操作(如将多次LPUSH合并为单条命令)。

    • 直接生成集合类型的完整数据命令(如SADD key mem1 mem2...)。

4. 子进程完成重写与父进程同步

  • 子进程完成新文件写入后,向父进程发送完成信号

  • 父进程收到信号后:

    1. 阻塞短暂时间,将aof_rewrite_buf缓冲区内的所有命令追加至子进程生成的新AOF文件末尾。

    2. 对新文件执行fsync() 确保数据落盘(受appendfsync配置影响)。

    3. 原子替换旧文件

      • 使用rename()系统调用将临时新文件原子性替换原始AOF文件。

      • 此后所有新命令写入替换后的文件。

当 Redis 正在执行 BGREWRITEAOF 时,如果当前已经有一个后台 AOF 重写 (BGREWRITEAOF) 正在进行。Redis会怎么做?

行为: Redis 不会启动另一个并行的重写操作。新的 BGREWRITEAOF 命令会直接返回(通常返回一个错误信息,表明重写已经在进行中)。

当 Redis 正在执行 BGREWRITEAOF 时,如果当前有一个后台 RDB 快照生成 (BGSAVE) 正在进行。Redis会怎么做?

行为: 此时发出的 BGREWRITEAOF 命令不会立即执行。它会等待,直到当前的 RDB 快照生成完成之后,再自动启动 AOF 重写操作。这是为了避免同时进行两个消耗大量磁盘 I/O 和 CPU (尤其是 fork 操作) 的后台进程,影响服务器性能。

RDB vs. AOF 在处理 fork 后数据上的差异:

  • RDB: 执行 BGSAVE 时,父进程 fork 出子进程。子进程将 fork 那一刻的内存数据快照写入 RDB 文件。对于 fork 之后父进程接收并处理的新写命令及其产生的数据变更,RDB 机制会“置之不理”。这意味着生成的 RDB 文件只包含快照时间点的数据。

  • AOF 重写 (BGREWRITEAOF): 同样会 fork 子进程。子进程基于 fork 那一刻的数据库状态开始构建新的 AOF 文件。关键区别在于,对于 fork 之后父进程接收的新写命令:

    • 父进程不仅会将这些命令追加到现有的 AOF 文件中(如果 AOF 开启),

    • 同时还会将它们写入一个专门的 aof_rewrite_buf 缓冲区

    • 当子进程完成新 AOF 文件的构建后,父进程会将 aof_rewrite_buf 缓冲区中的命令追加到新 AOF 文件的末尾。

    • 最后,Redis 用这个包含了快照点数据 + 增量命令的新 AOF 文件原子性地替换旧的 AOF 文件。

设计理念与适用场景:

  • RDB: 核心设计理念是定期备份。它生成的是某个时间点的完整数据快照。这种特性决定了它难以与最新的数据状态保持完全一致,在两次快照之间如果发生故障,数据会丢失。它的优势在于恢复速度快(加载一个大文件比回放大量命令快),文件通常更紧凑。

  • AOF: 核心设计理念是持久化记录所有写操作(类似于日志)。通过 appendfsync 配置策略(如 alwayseverysecno),它可以提供接近实时或准实时的数据持久化保证,数据安全性通常更高(取决于配置)。现代硬件资源通常比较充裕,AOF 带来的额外开销(主要是磁盘 I/O)在多数场景下是可以接受的。因此,在对数据安全性要求较高的场景中,AOF 或其混合模式(RDB+AOF)往往是更常见的选择。当然,定期备份(RDB)在需要历史快照或快速恢复的场景依然不可或缺。

父进程fork之后,就让子进程写新的AOF文件了,并且子进程生成新的AOF文件的时间也不久。为什么父进程必须继续写旧的 AOF 文件?

父进程在 fork 之后继续将新命令写入旧的 AOF 文件(同时写入 aof_rewrite_buf)是至关重要的。不能停止写入!

为什么呢?我们需要考虑极端清空

考虑极端情况:

假设 AOF 重写过程进行到一半时(子进程还在构建新的AOF文件),服务器突然崩溃(比如断电)。此时:

  1. 子进程内存中的数据会丢失,它构建的新 AOF 文件是不完整的、无效的

  2. 如果父进程在此期间停止了向旧的 AOF 文件写入新命令,那么:

    • 旧的 AOF 文件只包含到重写开始前的命令。

    • 重写开始后到崩溃前这段时间的所有新命令既不在旧 AOF 文件中,也不在新 AOF 文件中(因为新文件不完整)

    • 服务器重启后,Redis 加载旧的 AOF 文件,将丢失重写期间的所有数据变更!

我们可以使用一个跳槽的例子来理解这个过程

想象你在 A 公司工作(代表旧的 AOF 文件)。你决定跳槽去 B 公司(代表新的 AOF 文件),并进入了交接期(AOF 重写过程)。

  • 危险行为: 如果你在 A 公司交接期就完全停止认真工作(类比父进程停止写旧 AOF),把所有希望都寄托在 B 公司的 offer 上。

  • 极端风险: 如果在入职前夕,B 公司突然撤销了 offer(类比重写子进程崩溃/服务器崩溃),而此时你在 A 公司也已经“摆烂”待不下去了(旧 AOF 缺少重写期间的数据)。结果就是两头落空,损失了交接期(重写期)的所有“工作成果”(数据变更)。

  • 正确做法: 在 B 公司正式入职(新 AOF 文件原子替换成功)之前,你必须继续尽职尽责地完成在 A 公司的本职工作(父进程持续写入旧 AOF),即使你即将离开。这样,即使 B 公司的机会意外落空(重写失败),你依然保有在 A 公司的完整工作记录(旧 AOF 的完整数据)作为保障。

四.关于 Redis 持久化加载的优先级

当 Redis 配置中同时开启了 AOF (appendonly yes) 和 RDB(例如通过 save 配置或 bgsave 命令)持久化功能时,Redis 重启后恢复数据会优先使用 AOF 文件 (appendonly.aof)

这意味着:

  1. RDB 文件生成仍然有效: 配置的 RDB 快照规则(如 save 900 1)或手动执行的 SAVE/BGSAVE 命令仍然会生效,RDB 文件(通常是 dump.rdb仍然会被创建和更新。RDB 并没有被“禁用”或“关闭”。

  2. 重启加载优先级:AOF > RDB: 在 Redis 服务启动过程中,如果检测到 AOF 文件存在且可用(非空且未损坏),Redis 一定会选择加载 AOF 文件来恢复数据。它不会主动去加载 RDB 文件 (dump.rdb) 的内容。

  3. 原因:AOF 通常包含更完整的数据: Redis 做出这种设计决策的核心原因是:AOF 持久化通常能提供比 RDB 更高的数据安全性(例如配置了 appendfsync everysec)。AOF 文件记录了最后一次 RDB 快照之后(或服务启动以来)的所有写命令,因此它理论上包含了比最近一次的 RDB 快照更新、更完整的数据集。优先加载 AOF 可以最大程度地减少数据丢失。

  4. 回退机制(如果 AOF 不可用): 只有在 AOF 功能被显式关闭 (appendonly no) 或者 AOF 文件不存在(如首次启动)或存在严重损坏且无法修复 的情况下,Redis 在重启时才会回退(Fallback)到加载 RDB 文件 (dump.rdb) 来恢复数据。

  5. 混合持久化(Redis 4.0+)的特殊性:

    • 在 Redis 4.0 及以上版本,当开启了 AOF 并触发了 AOF 重写 (BGREWRITEAOF) 时,默认会使用 混合持久化 模式。

    • 在这种模式下,重写后生成的新 AOF 文件 的开头部分是一个 完整数据的 RDB 格式二进制快照,后面再追加从快照点之后到重写完成期间产生的增量写命令(AOF 格式)。

    • 即使在这个场景下,重启时加载的仍然是这个混合格式的 AOF 文件,而不是独立的 dump.rdb 文件。Redis 会先快速加载开头的 RDB 快照部分,然后再执行后面的 AOF 命令部分。核心原则“优先加载 AOF 文件”仍然成立,只是这个 AOF 文件内部嵌入了 RDB 数据。

总结关键点:

  • 开启 AOF 不会阻止 RDB 文件的生成。 RDB 快照机制照常工作。

  • 重启时,Redis 总是优先加载 AOF 文件 (appendonly.aof) 来恢复数据。 这是设计上的明确优先级

  • 仅当 AOF 不可用时(关闭、文件不存在/严重损坏),Redis 才会加载 RDB 文件 (dump.rdb)。

  • 混合持久化 (4.0+) 是 AOF 重写的一种优化形式,它生成的是一个特殊的 AOF 文件,重启时加载的依然是这个 AOF 文件。

因此,更精确的说法是: 当 AOF 持久化开启且其文件可用时,Redis 重启会优先并主动选择加载 AOF 文件,而忽略同时存在的 RDB 文件。RDB 文件在此时充当的是一个“备用”或“历史”快照的角色,或者在混合持久化中被部分嵌入到 AOF 文件中。

五.混合持久化

5.1.什么是混合持久化

Redis 的混合持久化(RDB-AOF Hybrid Persistence) 是 Redis 4.0 引入的一项革命性特性。它旨在结合 RDB 持久化和 AOF 持久化两者的优势,同时尽量规避各自的缺点,提供一种在数据安全性启动恢复速度上更优的解决方案。

核心目标:

  1. 快速重启恢复: 像 RDB 一样,能快速加载一个时间点的完整数据快照。

  2. 低数据丢失风险: 像 AOF 一样,能记录快照时间点之后的所有写操作,最大限度减少故障时的数据丢失。

  3. 文件相对紧凑: 比纯 AOF 文件小,比历史 RDB 文件能包含更多最新数据。

工作过程

混合持久化不是同时独立生成 RDB 和 AOF 文件,而是将 RDB 格式的数据嵌入到 AOF 文件中。具体过程如下:

  1. 开启混合持久化: 这是关键前提!在 Redis 配置文件 redis.conf 中设置:

    aof-use-rdb-preamble yes # 默认在 Redis 5+ 中通常是开启的
  2. 触发 AOF 重写 (BGREWRITEAOF):

    • 当满足 AOF 重写条件(文件大小增长比例、时间阈值等)时,或者手动执行 BGREWRITEAOF 命令时,混合持久化机制就会发挥作用。

    • 父进程 fork 出子进程。

  3. 子进程生成 RDB 快照:

    • 关键区别: 与传统的 AOF 重写(子进程遍历数据库生成写命令集)不同,在混合持久化开启时,子进程首先将当前内存数据的快照以 RDB 二进制格式写入到新的 AOF 文件的开头部分。这个过程和 BGSAVE 生成 RDB 文件完全一致。

    • 这个 RDB 数据块被称为 RDB preamble(RDB 前导块)

  4. 父进程处理增量命令:

    • 在子进程生成 RDB 快照期间,父进程像标准 AOF 重写一样,继续接收客户端的写命令。

    • 这些子进程开始之后产生的新写命令

      • 被父进程追加到现有的旧 AOF 文件中(保证崩溃时旧文件完整)。

      • 同时被写入 aof_rewrite_buf 缓冲区

  5. 子进程追加增量 AOF 命令:

    • 当子进程完成将内存快照写入新 AOF 文件(即写完 RDB preamble)后,它不会停止

    • 子进程会读取父进程 aof_rewrite_buf 缓冲区中积累的新写命令

    • 将这些新写命令以 AOF 协议格式(纯文本命令) 追加到刚才生成的 RDB 数据块后面。

  6. 原子替换:

    • 子进程完成整个新 AOF 文件的写入(RDB preamble + 增量 AOF 命令)后,通知父进程。

    • 父进程执行原子操作:用这个新的、混合格式的 AOF 文件替换旧的 AOF 文件。

5.2.见一见混合持久化

我们来看看这个混合持久化存储

接着我们去修改配置文件

vim /etc/redis/redis.conf

大家找到下面这个来

把它改成yes。

接下来我们需要重启redis服务器

service redis-server restart

然后我们需要去看看我们的工作目录

这个时候我们需要先记录变化前的情况

显然还是没有什么变化,这个时候我们需要去登陆客户端执行一些命令才会看到变化啊。

我们回去工作目录看看

????怎么出现了rdb后缀的文件,这就是混合持久化的特点啊。

我们先看看里面这两个文件

这两个文件和之前的inode和之前的不一样,说明都是新文件。

我们进去看看里面的内容

vim /var/lib/redis/appendonlydir/appendonly.aof.4.incr.aof

vim /var/lib/redis/appendonlydir/appendonly.aof.4.base.rdb

我们发现这个rdb文件的格式很像我们看到的rdb文件的格式啊。

接下来我们进入客户端去执行一些命令看看

我们回到这些文件看看

vim /var/lib/redis/appendonlydir/appendonly.aof.4.incr.aof

vim /var/lib/redis/appendonlydir/appendonly.aof.4.base.rdb

我们发现base文件没有变化,但是incr文件多了一些东西。这个和AOF是一样的。

这个时候我们查看一下文件的inode

相比于之前是没有任何变化的,说明还是老文件。

这个时候我们回客户端执行一个命令

然后我们回去看看文件

发现文件不存在,我们回目录看看

我们发现文件inode都发生了变化,这就是说明是新文件,这个和AOF是一样的。

我们进去看看里面的内容

 我们回到这些文件看看

vim /var/lib/redis/appendonlydir/appendonly.aof.5.incr.aof

vim /var/lib/redis/appendonlydir/appendonly.aof.5.base.rdb

这个时候我们发现base文件多了一些东西,但是这个incr文件也被清空了,这个和AOF是一样的。

从表面上看,混合持久化相比于AOF好像是将base文件的格式从.aof换成了.rdb格式。

可千万不要小扩这么一个格式变化。其实这个涉及到了底层的实现。

我们得知道redis底层生成.rdb文件的速度比.aof的速度要快得多,而且.rdb文件结构更为紧凑,数据恢复速度也是.rdb文件要快得多。

而rdb和aof的结合给混合持久化带来了下面这些核心优势:

  1. 极快的重启恢复速度: 大部分数据(通常是绝大部分)通过加载紧凑的二进制 RDB 块快速恢复,只有相对少量的增量命令需要重放。这比从头到尾重放一个完整的、可能非常大的纯 AOF 文件要快几个数量级

  2. 更低的数据丢失风险:

    • 仍然利用了 AOF 记录增量命令的能力。增量部分可以通过 appendfsync 配置(如 everysec 或 always)来控制丢失窗口。

    • 相比纯 RDB(两次快照间的数据全部丢失),丢失的数据量仅限于 AOF 增量部分尚未同步到磁盘的量。

  3. 文件相对较小:

    • RDB 格式本身比等效的 AOF 命令文本更紧凑。

    • 增量 AOF 部分只包含两次重写之间产生的命令,通常不会太长。

    • 总体积通常远小于纯 AOF 文件(除非重写频率极低或写入量巨大),但可能略大于同期的纯 RDB 文件(因为多了增量部分)。

  4. 兼具 RDB 和 AOF 优点: 在一个文件中同时获得了 RDB 的快速加载和 AOF 的近似实时持久化能力。

需要注意的细节:

  • 基于 AOF 重写: 混合持久化只在执行 BGREWRITEAOF 时才生成新的混合格式文件。普通的 AOF 追加写入(写入到 appendonly.aof)仍然是纯命令文本格式。

  • 兼容性: 混合格式的 AOF 文件只能被 Redis 4.0 及以上版本识别和加载。旧版本 Redis 会认为这是一个损坏的 AOF 文件(因为开头是二进制)。纯 RDB 文件(.rdb)和纯 AOF 文件仍然可以被兼容版本加载。

  • aof-use-rdb-preamble 配置: 必须明确设置为 yes 才能启用混合持久化。在 Redis 5.0 及以后,这个选项默认是开启的 (yes)。

  • 文件内容查看: 直接打开混合格式的 AOF 文件,开头是乱码(RDB 二进制),后面是明文命令。不要手动编辑它!

  • 备份: 备份混合格式的 AOF 文件即可,它包含了某个时间点的完整快照和后续增量。

http://www.lryc.cn/news/616407.html

相关文章:

  • RK3588在YOLO12(seg/pose/obb)推理任务中的加速方法
  • Kafka消费者相关原理
  • 纳维 - 斯托克斯方程的存在性与光滑性:流体世界的千年谜题
  • Python训练营打卡DAY 26 函数专题1:函数定义与参数
  • 大模型工具集成四层架构:识别、协议、执行与实现
  • JS中typeof与instanceof的区别
  • 专题三_二分_二分查找
  • 单片机捷径
  • Shell脚本-了解i++和++i
  • Linux常用命令(后端开发版)
  • NVIDIA Jetson AGX Orin 全景解析——边缘计算的高性能选择
  • 6A 工作流:让 Cursor、Trae 等AI编程助手按流程交付的实战手册
  • 机器学习——多元线性回归
  • React Profiler
  • HarmonyOS NEXT系列之编译三方C/C++库
  • 【Jenkins入门以及安装】
  • 《动手学深度学习》读书笔记—10.4 Bahdanau注意力
  • 移动端音频处理实践:59MB变声应用的技术实现分析
  • MySQL中的in和exists的区别
  • C++多线程服务器
  • Spring循环依赖详解
  • MySQL面试题及详细答案 155道(041-060)
  • LeeCode 46. 全排列
  • 冒泡排序实现以及优化
  • 20250810 | 深度学习入门笔记1
  • 大型动作模型LAM:让企业重复任务实现80%效率提升的AI技术架构与实现方案
  • 五种 IO 模型与阻塞 IO
  • 数组中的第K个最大元素
  • MyBatisPlus插件原理
  • Leetcode 3646. Next Special Palindrome Number