Bootstrap

深入探讨 Redis 持久化机制:原理、配置与优化策略

一、引言

Redis以高速缓存和数据存储的能力闻名,但其本身并不是传统意义上的关系型数据库,且数据存储主要在内存中,这意味着一旦系统重启或发生故障,内存中的数据将丢失。因此,对于很多生产环境中的使用场景,Redis的持久化机制变得尤为重要,确保数据的可靠性和持久存储。

  • 为什么持久化在Redis中重要,如何平衡性能与数据安全

在Redis中,持久化是指将内存中的数据存储到磁盘上,确保即使系统宕机或重启,数据也不会丢失。持久化不仅是为了数据的安全性,也是为了实现数据恢复、备份等功能。在某些场景下,数据丢失可能带来巨大的损失,因此,如何确保Redis中的数据在宕机时可以恢复,成为了一个重要的课题。

在这里插入图片描述

Redis提供了多种持久化方式,其中最常见的是RDB(快照)和AOF(日志)。每种方式都有其优点和缺点,RDB能够在保证高性能的同时保存数据的某一时刻的快照,而AOF则能够记录每个写操作,提供更高的数据安全性。混合持久化方式(结合RDB与AOF)则进一步平衡了性能与安全性。选择合适的持久化方式,需要根据具体应用场景的需求,权衡数据的可靠性与系统的性能需求。

总的来说,Redis的持久化方式允许用户在不牺牲性能的情况下,实现数据的可靠存储,确保系统在出现故障时能够恢复数据。


二、Redis持久化概述

Redis persistence,持久化是指将Redis中的数据从内存存储转移到磁盘上的过程。由于Redis是一个内存数据库,所有的数据默认存储在内存中,若发生系统崩溃或重启,内存中的数据将会丢失。因此,持久化机制可以确保Redis中的数据在出现故障后能够恢复。

持久化的主要作用包括:

  • 数据恢复:通过持久化机制,Redis能够在重启后恢复数据,避免因宕机或故障导致的数据丢失。
  • 数据备份:持久化文件可作为数据的备份,方便灾难恢复。
  • 持久存储:对一些需要长期存储的数据,可以通过持久化方式将数据从内存中转移到磁盘中,节省内存资源,同时保证数据不丢失。

持久性是指将数据写入持久存储,例如固态磁盘 (SSD)。Redis 提供了一系列持久性选项,具体包括以下几种:

  1. RDB(Redis Database)持久化: RDB持久化会在指定的时间间隔内对数据集执行即时时间点快照。
  • Redis通过定期生成一个数据集的快照(snapshotdump.rdb文件),该文件包含了某一时刻Redis的所有数据。Redis通过fork(创建子进程)来执行快照的生成操作,从而不阻塞主进程。
  • 快照是某一时刻数据的全量保存,适用于定期备份。
  1. AOF(Append Only File)持久化: AOF持久化记录服务器接收到的每个写操作。这些操作可以在服务器启动时再次重放,重建原始数据集。命令使用与Redis协议本身相同的格式进行记录。当Redis启动时,可以根据AOF日志文件来恢复数据。
    • 每当Redis执行写操作时,都会将该操作追加到AOF日志文件(appendonly.aof)中。恢复数据时,Redis读取AOF文件中的所有命令并重新执行,从而重建数据集。
  2. 无持久化: 可以完全禁用持久化。这在用作缓存时有时会被使用。
    • 可以完全禁用持久化,这样 Redis 数据只会保存在内存中,适用于缓存场景。
    • 数据不会被写入磁盘,在 Redis 重启时会丢失所有数据。
  3. RDB + AOF 结合: 可以在同一实例中结合使用AOF和RDB。
    • Redis在执行RDB快照时,会同时将AOF中的命令写入到RDB文件中,生成一个包含数据和部分AOF日志的混合文件。恢复时,Redis会优先读取RDB文件,并根据AOF日志中的命令来进一步恢复数据。

三、RDB(Redis DataBase)持久化

1、RDB概念与工作原理

RDB(Redis DataBase)是一种数据持久化方式,它通过周期性地将 Redis 数据集的快照保存到磁盘上的一个二进制文件(通常为 dump.rdb)来保证数据的持久化。也就是说它将内存中的所有数据一次性保存到磁盘,适用于数据备份、灾难恢复等场景。快照可以看作是某一时刻的数据备份,通过保存数据集的副本,可以在系统发生故障时进行恢复。

在这里插入图片描述

RDB 持久化的特点是通过全量快照的方式保存数据,也就是说它将内存中的所有数据一次性保存到磁盘,适用于数据备份、灾难恢复等场景。

也就是,在指定的时间间隔,执行数据集的时间点快照。实现类似照片记录效果的方式,就是把某一时刻的数据和状态以文件的形式写到磁盘上,也就是快照。这样一来即使故障宕机,快照文件也不会丢失,数据的可靠性也就得到了保证。这个快照文件就称为RDB文件。

Redis的数据都在内存中,保存备份时它执行的是全量快照,也就是说,把内存中的所有数据都记录到磁盘中,一锅端。默认情况下,保存在一个名为 dump.rdb 的二进制文件中。

工作原理

当 Redis 需要将数据集保存到磁盘时,执行的过程如下:

  1. fork:Redis 会创建一个子进程,这时会有一个父进程和一个子进程。此时父进程继续处理客户端请求,而子进程则负责执行数据快照的操作。
  2. 子进程写入数据:子进程开始将内存中的数据集写入一个临时的 RDB 文件中,通常命名为 dump.rdb。此时,父进程的内存数据并不会受到影响,父进程继续响应客户端的操作。
  3. 完成替换:当子进程完成 RDB 文件的写入后,它会将新的文件替换掉旧的 dump.rdb 文件。由于采用了 写时复制(Copy-on-Write) 机制,父进程的内存数据在子进程写入时并不需要被锁住,因此 Redis 能够保证高效和低延迟。
  4. 存储在磁盘上:最终,dump.rdb 文件保存了 Redis 当前的数据集,它被保存在磁盘上,可以用于在 Redis 重启后恢复数据。

这个过程利用了 写时拷贝(Copy-on-Write) 语义。当子进程开始写入数据时,父进程仍然可以继续服务客户端。这样,Redis 可以确保父进程不会受到数据写入的影响,同时可以在后台异步地完成数据持久化操作。写时拷贝点击此处了解

这种方式的优势是生成的 RDB 文件非常小,适合进行数据备份和灾难恢复。然而,RDB 持久化会在发生 Redis 异常关闭时,丢失最近一次快照之后的所有操作。因此,它在需要强一致性保证的场景下,可能并不适用。

2、RDB的配置选项

RDB的操作分为自动触发手动触发

自动触发

RDB会在 Redis 数据发生修改时对内存中的数据进行快照,然后保存到磁盘,以保证数据的持久性。Redis 会根据配置文件中的 save 选项,自动触发 RDB 快照保存操作。当满足一定条件时(例如在指定的时间间隔内修改了指定数量的数据),Redis 会自动将当前数据集保存到磁盘上的 dump.rdb 文件中。具体触发条件由 save 配置项定义。

通常情况下,RDB 保存快照的时间间隔由配置文件中的参数 save 决定,格式为 save <seconds> <changes>,表示在 <seconds> 秒内,如果数据有 <changes> 次修改,则会进行一次快照。

超过xx秒以后如果达到了xx次数, 就会自动触发, 也就是说必须满足两个条件: 一个是距离上次更新超过了xx秒, 一个是在这段时间里达到了xx次数, 满足这两个条件才会自动触发save。

在这里插入图片描述

可以配置 Redis,在数据集发生至少 M 次变化时,每 N 秒保存一次数据集。例如,在 redis.conf 配置文件中可以配置以下内容:

save 900 1
save 300 10
save 60 1000

这表示:

  • 如果 15 分钟内至少有 1 次写操作,Redis 会自动保存快照。
  • 如果 5 分钟内有至少 10 次写操作,Redis 会自动保存快照。
  • 如果 1 分钟内有至少 1000 次写操作,Redis 会自动保存快照。

自动触发的 RDB 保存操作不需要手动干预,只要 Redis 的运行过程中满足相应的条件,它会在后台自动完成。

dump文件生成路径:

在这里插入图片描述

dir指定了 RDB 和 AOF 文件的存储目录。

默认情况下,Redis 的 dump.rdb 文件存储在 Redis 数据目录下,该目录由 dir 配置项指定。在 Redis 配置文件 redis.conf 中,dir 配置项用于设置数据文件的存储路径。

dump文件名:

在这里插入图片描述

默认情况下,Redis 会将快照数据保存为 dump.rdb 文件,且文件名是固定的。如果需要自定义文件名,可以在 Redis 配置文件中通过 dbfilename 配置项来设置。

  • 生成路径:通过 dir 配置项指定。
  • 文件名:通过 dbfilename 配置项指定,默认是 dump.rdb

手动触发

除了自动保存,Redis 还允许通过命令手动触发 RDB 保存操作。主要有两种命令:

  • SAVE:该命令会阻塞 Redis 进程,直到 RDB 快照保存完成。在执行 SAVE 命令时,Redis 会暂停处理其他客户端请求,直到将数据集的快照写入磁盘并保存为 dump.rdb 文件为止。
    • 使用 SAVE 命令时,Redis 会进行全量的数据保存,阻塞过程可能会影响 Redis 性能,因此在高并发情况下使用时需要谨慎。
  • BGSAVE:与 SAVE 不同,BGSAVE 是在后台执行的,不会阻塞 Redis 的主进程。执行 BGSAVE 时,Redis 会通过 fork() 创建一个子进程,子进程负责将数据集的快照写入磁盘,而主进程则继续响应客户端请求。这样,BGSAVE 不会影响 Redis 的实时性能。
    • BGSAVE 适用于希望在不阻塞 Redis 进程的情况下进行快照操作的场景。

执行 flushallflushdbshutdown 命令时,确实也会产生一个空的 dump.rdb 文件。这是因为 Redis 在执行这些命令时,通常会执行以下操作:

  1. flushall:清空所有数据库的键值对。
  2. flushdb:清空当前数据库中的所有键值对。
  3. shutdown:关闭 Redis 服务

LASTSAVE:可以通过lastsave命令获取最后一次成功执行快照的UNIX时间戳。

如何使用 RDB 文件恢复 Redis 数据

将备份的 dump.rdb 文件放置到 Redis 数据目录中。默认情况下,Redis 会在配置文件中的指定路径中查找 dump.rdb 文件。

如果 RDB 文件存放在其他目录,确保将 dump.rdb 文件放置到 Redis 配置中的 dir 目录下。将 dump.rdb 文件放到正确的目录后,重新启动 Redis 服务,即可恢复数据。

在进行Redis的物理恢复时,确实需要注意备份文件和生产服务器之间的隔离存储。确保备份文件(如dump.rdb)存放在与生产环境分开的存储设备上,这样即使生产服务器发生故障,备份文件也能得到保护,避免数据丢失。

步骤如下:

  1. 备份: 在正常运行的Redis实例上生成RDB快照(dump.rdb),并将该文件保存到专门的备份服务器或存储设备上。
  2. 清空Redis数据: 在测试恢复时,可以通过执行flushdbflushall命令来清空Redis的数据,模拟数据丢失的场景。
  3. 恢复: 将备份的dump.rdb文件移动到Redis配置文件中指定的目录,并启动Redis服务,Redis会自动从dump.rdb文件中恢复数据。

通过这种方式,你可以验证Redis的物理恢复过程,确保备份文件的有效性,并避免因存储位置不当导致的数据丢失风险。

总结一下,哪些情况下会触发RBD快照?

  1. 配置文件中的快照配置 (save 配置)
    • Redis通过配置文件中的save选项来设定自动保存快照的条件。每当满足指定的时间间隔和数据变化条件时,Redis会触发RDB快照。
    • 默认配置(redis.conf 文件中的 save 选项):如果在指定的时间间隔内有足够数量的写操作发生,则触发RDB快照。
  2. 手动触发 SAVEBGSAVE 命令
  3. 执行 FLUSHDBFLUSHALL 命令:在恢复时,空的 dump.rdb 文件会被加载,这时Redis数据库中会是空的。
  4. 执行 SHUTDOWN 且没有开启AOF持久化:当执行 SHUTDOWN 命令时,如果没有启用AOF持久化,Redis会尝试将当前内存中的数据保存到 dump.rdb 文件中。如果成功,文件会包含所有数据;如果失败(如磁盘空间不足等),则数据不会被保存。
  5. 主从复制时,主节点自动触发快照:在主从复制场景中,主节点会自动生成快照并保存到RDB文件。当主节点的RDB文件被修改时,主节点会将这些变更传送给从节点,确保从节点能够从RDB文件中恢复数据。

3、RDB优化配置项

配置文件中SNAPSHOTTING模块:

  1. save

    • 功能:设置触发RDB快照保存的条件。

    • 格式save <seconds> <changes>

    • 作用:该选项用于指定在多少秒内,如果有至少多少次写操作发生,Redis就会触发保存RDB快照。例如:

      • save 3600 1:每隔1小时(3600秒)如果有至少1次写操作发生,就触发保存RDB快照。
    • 注意:当Redis满足某个save条件时,就会进行一次快照保存操作,默认情况下,Redis会在多次写操作后自动进行快照。

  2. dir

    • 功能:设置RDB文件和AOF文件保存的目录。

    • 格式dir <path>

    • 作用:指定Redis实例的工作目录,即存储RDB快照文件、AOF日志文件和其他相关数据的路径。默认路径为当前工作目录(./)。

      • 例如:dir /var/lib/redis/ 可以将RDB和AOF文件存储到 /var/lib/redis/ 目录下。
  3. dbfilename

    • 功能:设置RDB快照的文件名。

    • 格式dbfilename <filename>

    • 作用:配置生成的RDB文件名。默认情况下,RDB文件名是 dump.rdb。

      • 例如:dbfilename mydata.rdb 可以将RDB快照文件命名为 mydata.rdb
  4. stop-writes-on-bgsave-error

    • 功能:在RDB快照保存失败时,是否阻止写操作。

    • 格式stop-writes-on-bgsave-error <yes|no>

    • 作用:默认情况下该选项为yes,即在RDB保存失败时,Redis会停止接受写操作,并向客户端报告错误。这是为了防止数据丢失,因为如果RDB保存失败,Redis无法保证数据已持久化到磁盘。

      • 如果设置为no,即使快照保存失败,Redis仍然会继续接受写操作,保证服务不中断,但会增加数据丢失的风险。因此,通常建议将该选项保留为yes,以便及时发现数据保存失败的问题。
      • 配置示例:stop-writes-on-bgsave-error no 表示不管快照保存失败,Redis仍然允许继续接受写请求。
  5. rdbcompression

    • 功能:控制是否对RDB文件进行压缩。

    • 格式rdbcompression <yes|no>

    • 作用:默认情况下,Redis会启用压缩(使用LZF算法)来减少RDB文件的大小。压缩会节省磁盘空间,但会消耗一些CPU资源。如果不想消耗CPU来进行压缩,可以将该选项设置为no,这样会减少CPU开销,但RDB文件会变得更大。

    • 配置示例:rdbcompression no 表示关闭RDB文件的压缩。

  6. rdbchecksum

    • 功能:是否启用RDB文件的校验和。

    • 格式rdbchecksum <yes|no>

    • 作用:默认情况下,Redis会在RDB文件的末尾添加一个CRC64校验和,以确保文件的完整性。启用校验和可以有效防止RDB文件损坏,但会带来约10%的性能损耗。如果希望最大化性能,可以关闭此功能。

    • 配置示例:rdbchecksum no 表示关闭RDB文件的校验和。

  7. rdb-del-sync-files

    • 功能:删除复制过程中使用的RDB文件(在没有持久化的情况下)。

    • 格式rdb-del-sync-files <yes|no>

    • 作用:该选项用于在没有持久化(即没有开启RDB和AOF持久化)的情况下,是否删除复制过程中使用的RDB文件。默认情况下为no,即不删除这些RDB文件。如果配置为yes,Redis会在不需要持久化的情况下,删除同步过程中产生的RDB文件。该选项主要适用于一些特殊环境,比如出于安全或合规性要求,希望在没有持久化的情况下删除这些RDB文件。

    • 配置示例:rdb-del-sync-files yes 表示在不启用持久化的情况下删除复制同步使用的RDB文件。

通过调整这些RDB相关的配置项,可以根据应用的需要优化RDB的性能和持久化策略。需要根据具体场景来决定是否启用压缩、校验和,或是否允许在RDB保存失败时继续接受写操作。

如何禁用快照

  1. 动态禁用RDB快照(使用 config set 命令)

Redis提供了一个命令 config set,允许动态修改配置项。通过该命令,可以临时禁用RDB快照的保存规则。

redis-cli config set save ""

该命令会将 save 配置项设置为空,从而禁用所有的自动RDB快照保存规则。这种方式会立即生效,但只会持续到Redis重启,重启后会恢复为配置文件中的默认设置。

  1. 修改配置文件禁用RDB快照

除了动态修改配置外,还可以通过手动修改Redis的配置文件(通常是 redis.conf)来禁用RDB快照。

操作步骤

  1. 打开Redis配置文件(通常是 redis.conf),找到以下部分:

    # save <seconds> <changes>
    
  2. save 行注释掉或删除,或者将其设置为空字符串:

    save ""
    
  3. 保存并关闭文件。

  4. 重启Redis服务,使配置生效。

通过这种方式禁用快照后,Redis将不会在任何情况下自动保存RDB快照。修改后的配置会在Redis重启后生效,直到再次修改为止。

4、RDB的优势与劣势

RDB的优点

  • 紧凑的文件格式:RDB是Redis数据的紧凑单一时间点表示。RDB文件非常适合用于备份。例如,可能希望每小时归档一次RDB文件,保存过去24小时的数据快照,每天保存一个RDB快照,持续30天。这样,在发生灾难时,可以轻松地恢复不同版本的数据集。
  • 灾难恢复:RDB非常适合用于灾难恢复,它是一个紧凑的单一文件,可以传输到远程数据中心,或者上传到Amazon S3(可能需要加密)。这样可以确保数据在灾难发生时能够快速恢复。
  • 最大化性能:RDB通过将持久化任务交给子进程完成,最大化了Redis的性能。父进程只需要执行fork操作来生成子进程进行数据持久化,自己不会执行任何磁盘IO操作。
  • 更快的重启:与AOF相比,RDB允许在处理大型数据集时实现更快的重启。
  • 支持副本的部分重同步:在Redis副本上,RDB支持在重启和故障转移后进行部分重同步。

小总结:

  • 适合大规模的数据恢复。
  • 按照业务定时备份。
  • 对数据完整性和一致性要求不高。
  • RDB文件在内存中的加载速度要比AOF快很多。

RDB的缺点

  • 数据丢失风险:如果需要最大限度地减少Redis停机时数据丢失的风险(例如,发生断电时),RDB就不适合。可以配置不同的保存点,以便在特定条件下生成RDB文件(例如,每5分钟或进行至少100次写操作后)。但是,通常RDB快照是每五分钟或更长时间生成一次,因此,如果Redis在没有正确关闭的情况下停止工作,应该准备好丢失最近几分钟的数据。
  • fork()操作的性能问题:RDB需要频繁调用fork()来生成子进程以持久化数据。如果数据集非常大,fork()可能会消耗较多时间,导致Redis停止为客户端提供服务几毫秒,甚至可能长达一秒钟,尤其是在CPU性能较差时。虽然AOF也需要fork()操作,但它的频率较低,且可以调节重写日志的频率,而不需要对持久性进行任何权衡。

小总结:

  • 在一定间隔时间做一次备份,所以如果redis意外宕机或死机的话,就会丢失从当前至最近一次快照期间的数据,快照之间的数据会丢失。
  • 内存数据的全量同步,如果数据量太大会导致IO严重影响服务器性能。
  • RDB依赖于主进程的fork,在更大的数据集中,这可能会导致服务请求的瞬间延迟。fork的时候内存中的数据被克隆了一份,大致2倍的膨胀性,需要考虑。

三、AOF(Append-Only File)持久化

1、AOF概念与工作原理

RDB 方式存在一个问题:在系统崩溃、突然断电或进程被强制终止(例如使用 kill -9)时,Redis最近的写操作可能会丢失。这对于某些应用来说,丢失一些数据可能并不重要,但对于需要完全持久化数据的场景,RDB并不是一个足够可靠的方案。

为了解决这个问题,Redis引入了Append-only File(AOF) 机制,作为一种完整的持久化解决方案。AOF确保每个写操作都被持久化到磁盘,恢复时通过重放这些命令来重建数据状态。

AOF(Append-Only File)是一种持久化机制,用于记录所有写操作的日志。每当客户端对Redis执行写命令时,Redis会将该操作以命令的形式追加到AOF日志文件中。AOF保证了在发生故障时,通过重放这些命令,可以将Redis数据恢复到故障前的状态。

Redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

默认情况下,redis是没有开启AOF的。开启AOF功能需要设置配置:appendonly yes

在这里插入图片描述

AOF是Redis的一种替代的、完全持久化的策略。可以在配置文件中开启AOF:

appendonly yes

这将启用AOF机制,使Redis在接收到改变数据集的命令时(如 SETLPUSH 等),将该命令追加到AOF文件中。

从现在开始,每当Redis接收到一个改变数据集的命令(例如SET)时,它都会将该命令追加到AOF中。当重新启动Redis时,它会重新执行AOF来重建状态。

AOF 持久化功能的实现可以简单分为 5 步:

  1. 命令追加(append):所有的写命令会追加到 AOF 缓冲区中。
  2. 文件写入(write):将 AOF 缓冲区的数据写入到 AOF 文件中。这一步需要调用write函数(系统调用),write将数据写入到了系统内核缓冲区之后直接返回了(延迟写)。注意!!!此时并没有同步到磁盘。
  3. 文件同步(fsync):AOF 缓冲区根据对应的持久化方式( fsync 策略)向硬盘做同步操作。这一步需要调用 fsync 函数(系统调用), fsync 针对单个文件操作,对其进行强制硬盘同步,fsync 将阻塞直到写入磁盘完成后返回,保证了数据持久化。
  4. 文件重写(rewrite):随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
  5. 重启加载(load):当 Redis 重启时,可以加载 AOF 文件进行数据恢复。

AOF相比RDB有一个显著的优势,它能保证更少的数据丢失,因为它记录了所有对数据的修改操作,而不仅仅是数据的快照。具体来说,AOF机制会将每个写操作都以明文的Redis命令格式追加到AOF日志文件中,当Redis恢复时,会重新执行这些命令来恢复数据。读操作不记录。

AOF如何写入命令到日志文件中

自从Redis 7.0 版本开始,Redis使用多部分AOF机制。也就是说,原来的单个AOF文件被分割成一个基础文件(最多一个)和增量文件(可能有多个)。

  • 基础AOF文件(Base File):表示重写时数据集的初始快照。
  • 增量AOF文件(Incremental Files):记录从上次基础AOF文件以来的数据增量。

基础文件代表在AOF重写时数据存在的初始(RDB或AOF格式)快照。增量文件包含自上次创建基础AOF文件以来的增量变化。所有这些文件被放在一个单独的目录下,并通过一个清单文件进行跟踪。

在这里插入图片描述

  1. 客户端执行写命令:当Redis接收到客户端发起的写命令(如 SETLPUSH 等),Redis并不会立即执行持久化操作,而是将该命令追加到AOF日志文件中。

  2. 命令追加到AOF日志:在这些命令到达 Redis Server 以后并不是直接写入AOF文件,会将其这些命令先放入AOF缓存中进行保存。这里的AOF缓冲区实际上是内存中的一片区域,存在的目的是当这些命令达到一定量以后再写入磁盘,避免频繁的磁盘IO操作。

    • 每当Redis执行一个写操作时,会将该操作以命令行的形式追加到AOF日志文件。AOF文件的格式与Redis的协议格式一致,也就是纯文本格式,记录每个写操作。

    • AOF缓冲会根据AOF缓冲区同步文件的三种写回策略将命令写入磁盘上的AOF文件。

  3. 文件合并:随着写入AOF内容的增加为避免文件膨胀,会根据规则进行命令的合并(又称AOF重写),从而起到AOF文件压缩的目的。

  4. AOF恢复:当Redis重启时,会自动读取AOF文件并按顺序重放文件中的命令,以恢复到崩溃前的数据状态。

2、AOF的三种写回策略

在操作系统中,fsync() 调用是告诉操作系统将数据真正写入磁盘,而不是仅仅等待更多的数据填充到输出缓冲区中。不同的操作系统对 fsync() 的处理方式可能不同,有些操作系统会立即将数据刷新到磁盘,而有些操作系统则可能会尽快执行这个操作,但并不保证立刻写入。

在 Redis 中,appendfsync 用于指定何时将数据写入磁盘。Redis 提供了三种不同的 appendfsync 模式,每种模式在性能和数据安全性之间做出不同的平衡:

  • ALways:每次命令写入时都进行fsync,虽然非常安全,但会导致极慢的性能。
  • everysec:每秒进行一次fsync,速度较快,默认也是这种配置。最多可能丢失1秒的数据。
  • no:不进行fsync,而是依赖操作系统来管理文件刷盘,这种方式最快,但数据最不安全。

这 3 种写回策略都无法能完美解决「主进程阻塞」和「减少数据丢失」的问题,因为两个问题是对立的,偏向于一边的话,就会要牺牲另外一边,原因如下:

  • Always 策略的话,可以最大程度保证数据不丢失,但是由于它每执行一条写操作命令就同步将 AOF 内容写回硬盘,所以是不可避免会影响主进程的性能;
  • No 策略的话,是交由操作系统来决定何时将 AOF 日志内容写回硬盘,相比于 Always 策略性能较好,但是操作系统写回硬盘的时机是不可预知的,如果 AOF 日志内容没有写回硬盘,一旦服务器宕机,就会丢失不定数量的数据。
  • Everysec 策略的话,是折中的一种方式,避免了 Always 策略的性能开销,也比 No 策略更能避免数据丢失,当然如果上一秒的写操作命令日志没有写回到硬盘,发生了宕机,这一秒内的数据自然也会丢失。
    在这里插入图片描述

这三种策略仅仅只是在控制fsync()函数的调用时机。当应用程序向文件写入数据时,内核通常先将数据复制到内核缓冲区中,然后排入队列,然后由内核决定何时写入硬盘。

如果想要应用程序向文件写入数据后,能立马将数据同步到硬盘,就可以调用 fsync() 函数,这样内核就会将内核缓冲区的数据直接写入到硬盘,等到硬盘写操作完成后,该函数才会返回。

  • Always 策略就是每次写入 AOF 文件数据后,就执行 fsync() 函数;
  • Everysec 策略就会创建一个异步任务来执行 fsync() 函数;
  • No 策略就是永不执行 fsync() 函数;

3、Redis7 Multi Part AOF的设计

Redis6及以前 ,有且仅有一个。 appendonly.aof

自 Redis 7.0.0 起,Redis 引入了 多部分 AOF (Append-Only File) 机制,原本单一的 AOF 文件被拆分为多个文件,主要包括 基础文件 (base file)增量文件 (incremental file)。这些文件存储了 Redis 数据集的不同状态和变更,并通过 清单文件 (manifest file) 进行跟踪。
在这里插入图片描述

MP-AOF实现 方案概述 顾名思义,MP-AOF就是将原来的单个AOF文件拆分成多个AOF文件。在MP-AOF中,我们将AOF分为三种类型, 分别为:

  1. 基础文件 (Base File)

    • 基础文件 是一个完整的数据集快照,表示在该文件创建时数据集的完整状态。

    • 基础文件可以是 RDB 格式(二进制序列化的快照)或 AOF 格式(文本命令的记录)。

    • 当 Redis 执行 AOF 重写时,会创建一个新的基础文件,包含当时数据集的最小操作集。

  2. 增量文件 (Incremental File)

    • 增量文件 记录的是 基础文件之后 对数据集进行的增量变更。

    • 这些文件存储的是从上一个基础文件开始的所有新增命令,通常是写操作,如 SET、INCR 等。

    • 可以有多个增量文件,具体数量取决于数据的变更次数。

  3. 清单文件 (Manifest File)

    • 清单文件 用于追踪和记录所有 AOF 文件的创建顺序和使用顺序。
    • 清单文件会列出哪些文件属于当前的数据集,并确保 Redis 在恢复时可以正确加载这些文件,以重建数据集。

文件命名规则

Redis 7.0.0+ 中的 AOF 文件会根据 appendfilename 配置的文件名前缀生成文件名。文件名的结构包括以下几种:

  1. 清单文件append.aof.manifest,这是一个清单文件,用于追踪所有生成的 AOF 文件(包括基础文件和增量文件)。它记录了重写过程中的文件结构以及文件的顺序。这个文件是 Redis 7.0 及以上版本引入的多部分 AOF 机制的一部分,用于帮助 Redis 管理 AOF 文件的生命周期。
  2. 基础文件append.aof.1.base.rdb,这个文件是新的基础 AOF 文件,通常是一个包含数据库状态快照的文件。这个文件会包含一个经过压缩后的数据库快照,表示当前数据库的完整状态,或者是 AOF 重写过程中生成的最简命令集。它是 AOF 重写的核心部分,包含了数据库在某一时刻的完整状态。
  3. 增量文件append.aof.1.incr.aof,这是增量 AOF 文件,包含了自上一个基础 AOF 文件以来对数据库进行的所有变更(增量命令)。这些命令是 Redis 在执行 AOF 重写期间生成的,用于记录数据库中发生的任何写操作。

例如,假设 appendfilename 设置为 appendonly.aof,Redis 可能会生成如下文件:

  • appendonly.aof.1.base.rdb:第一个基础文件,格式为 RDB。
  • appendonly.aof.1.incr.aof:第一个增量文件,格式为 AOF。
  • appendonly.aof.manifest:清单文件,记录这些文件的顺序和关系。

Redis7.0 config 中对应的配置项:

// 几种类型的文件的前缀,后跟有关序列和类型的附加信息
appendfilename "appendonly.aof"
    
// 新版本增加的目录配置项目
appenddirname "appendonlydir"
    
// 如有下的aof文件存在
1. 基本文件
    append.aof.1.base.rbd
2.增量文件
    append.aof.1.incr.aof
3.清单文件
    append.aof.manifest 

4、AOF重写 (Log Rewriting)机制

由于AOF持久化是Redis不断将写命令记录到 AOF 文件中,随着Redis的运行,AOF文件会不断增大。每次写操作都会将命令追加到AOF文件中。例如,如果执行了100次自增操作,虽然最终只有一个键值存储了结果,AOF文件中却会记录100条命令。对于重建当前数据集来说,其中的99条命令是不必要的。因此,需要一种机制来定期对AOF日志进行重写,以避免AOF文件膨胀。

为了解决这个问题,Redis新增了重写机制,当AOF文件的大小超过所设定的峰值时,Redis就会自动启动AOF文件的内容压缩.只保留可以恢复数据的最小指令集或者可以手动使用命令 bgrewriteaof 来重新

也就是说,启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。

在 Redis 配置文件中,关于 自动重写 AOF 文件 的配置项如下所示:

在这里插入图片描述

注意:同时满足两种情况才会触发。

auto-aof-rewrite-percentage:这个配置项指定了 AOF 文件大小增长的百分比,超过该百分比后,Redis 会自动触发 AOF 文件重写(BGREWRITEAOF)。重写操作会在 AOF 文件增大的情况下进行,以保持 AOF 文件的优化。

  • 例如,auto-aof-rewrite-percentage 100 表示当 AOF 文件的大小增长超过上次重写时大小的 100%(即翻倍)时,Redis 会自动触发 AOF 文件重写。

auto-aof-rewrite-min-size:这个配置项指定了 AOF 文件的最小大小,只有当 AOF 文件的大小超过该最小值时,才会触发自动重写。如果 AOF 文件的增长达到了指定百分比,但文件大小仍然小于此阈值,则不会进行重写。

  • 例如,auto-aof-rewrite-min-size 64mb 表示 AOF 文件的大小必须至少为 64MB,才会考虑进行自动重写。如果 AOF 文件大小小于 64MB,即使它的大小增长了 100%,也不会触发重写。

AOF重写可以手动触发也可以自动触发

  • 自动触发:满足配置文件中的选项后,Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时(即同时触发才会重写,如果其中任何一个条件不满足,则不会触发重写操作。)。
  • 手动触发:可以通过BGREWRITEAOF命令手动触发AOF日志的重写。这个命令会在后台启动AOF重写过程,并生成包含最小操作集的新AOF文件。

工作原理

  1. 创建重写子进程:在 Redis 执行 AOF 重写时,首先会创建一个”重写子进程“。这个子进程会从当前的 AOF 文件中读取所有的写入指令,分析这些指令并进行压缩。压缩的目的是去掉不必要的冗余指令,并且只保留必要的最小指令集,从而减小文件的大小。例如,如果多个 INCR 操作被应用于同一个键,这些操作会被合并成一个操作,避免冗余指令的存储。

  2. 主进程继续写入原有的 AOF 文件:与此同时,Redis 的主进程会继续正常处理来自客户端的写操作,将新的写命令追加到原来的 AOF 文件中,并将这些命令缓存到内存中。这一过程保证了即使在进行 AOF 重写的同时,数据的持续写入也不会丢失。这样,原有的 AOF 文件仍然可以作为数据的可靠备份。

  3. 重写子进程完成重写后通知父进程:当子进程完成对 AOF 文件的重写后,它会向父进程发送一个信号,通知父进程重写工作已经完成。子进程创建的新 AOF 文件只包含当前数据库状态的最小指令集,而不会包含冗余的历史操作。

  4. 主进程将缓存的指令追加到新 AOF 文件:一旦父进程收到信号,表明子进程已经完成重写,父进程会将内存中缓冲的新增指令(即在重写过程中追加到原有 AOF 文件的命令)追加到新的 AOF 文件中。这一步骤确保了即使在重写过程中有新的操作,最终的新 AOF 文件也能包含所有的数据变更。

  5. 替换旧的 AOF 文件:当所有新的命令被追加到新 AOF 文件后,Redis 会使用新文件替换旧文件。这个替换操作是原子性的,确保在任何时刻 Redis 都有一个有效的 AOF 文件。当新的 AOF 文件替换完成后,所有后续的写操作将继续追加到新的 AOF 文件中。

当我们设计 aof-use-rdb-preambleno时,重启Redis,我们会发现在 配置文件中配置的dirappenddirname 组合形成的文件夹下面会出现三个文件。

当重新启动 Redis 并根据配置文件中的 dirappendfilename 配置查看目录时,会看到以下文件:

  • 增量文件append.aof.1.incr.aof
  • 清单文件append.aof.manifest
  • 新的增量文件append.aof.1.incr.aof

aof-use-rdb-preamble 设置为 no 时,表示 Redis 不会在 AOF 文件的开头使用 RDB 数据库快照,即 AOF 文件不会包含一个来自 RDB 快照的前导部分。默认情况下(如果为 yes),Redis 会将 RDB 快照嵌入到 AOF 文件的开始部分,作为基准数据。

禁用该选项 后,AOF 文件将仅通过写操作(增量文件)来记录数据,因此它不会包含 RDB 快照,AOF 重写时也不会包含这种预先的基准快照。

增量文件 (append.aof.1.incr.aof) 达到一定的大小阈值时,Redis 会触发 AOF 重写操作。在重写过程中,Redis 会通过以下步骤进行文件替换和生成新的文件:

  1. 创建新的基础文件append.aof.2.base.aof):Redis 会根据当前的数据库状态生成一个新的基础文件,包含最简的命令集,这个文件只会包含当前数据库状态的必要命令,去除多余的历史命令。
    • 这个新生成的基础文件会被标记为 append.aof.2.base.aof
  2. 生成新的增量文件append.aof.2.incr.aof):生成一个新的增量文件用于记录从上次基础文件创建后的增量操作。在生成新基础文件的过程中,Redis 会将新写入的操作追加到这个新的增量文件中。
    • 在重写完成后,新的增量文件的大小通常是从零开始,随着新的写入操作逐渐增大。
  3. 删除旧的增量文件append.aof.1.incr.aof):在文件重写完成后,Redis 会删除旧的增量文件和基础文件(例如,append.aof.1.base.aofappend.aof.1.incr.aof),并将新的基础文件和增量文件(例如,append.aof.2.base.aofappend.aof.2.incr.aof)替换掉旧的文件。
  4. 清单文件更新append.aof.manifest):清单文件会更新,记录新的基础文件和增量文件的名称和顺序,以及它们在文件系统中的位置。
    • 这个文件的作用是为了追踪多个 AOF 文件的历史,确保 Redis 恢复时能正确地按照顺序应用文件内容。

也就是说AOF文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的AOF文件。

AOF重写使用了与快照(RDB)类似的写时复制(copy-on-write)机制。Redis会创建一个子进程来执行AOF重写,而主进程则继续处理正常的客户端请求。

  • Redis 7.0及以上版本:父进程继续将增量更新写入增量AOF文件,子进程生成新的基础AOF文件。重写完成后,父进程会交换增量AOF文件和基础AOF文件,确保原子替换。
  • Redis 7.0以下版本:父进程将增量数据继续写入旧AOF文件,而子进程在后台生成新的AOF文件。完成后,父进程将内存中的增量数据追加到新的文件并替换掉旧文件。

Redis 在进行 AOF(Append-Only File)重写时,采用了与快照(RDB)类似的 “写时复制”(copy-on-write,COW)技术来保证在重写过程中的数据一致性和安全性。这个过程对于不同版本的 Redis(7.0 及之后版本与 7.0 之前的版本)有所不同。下面我们对两种版本的 AOF 重写过程进行详细分析:

Redis >= 7.0 的 AOF 重写过程

  1. 创建子进程
    Redis 在启动 AOF 重写时,会通过 fork 操作创建两个进程:
  • 子进程:负责生成新的 AOF 基础文件(即完整的数据快照)。
  • 父进程:继续接收客户端的写入操作,并将这些操作写入一个新的增量 AOF 文件。
  1. 增量文件与基础文件
  • 子进程:在新基础文件中重写原始数据,即将当前的数据集以新的、精简的 AOF 命令重新写入到一个临时文件中。
  • 父进程:与此同时,父进程会打开一个新的增量文件来写入来自客户端的所有新操作,这些操作会被追加到增量文件中。
  1. 重写失败保护
  • 如果在 AOF 重写过程中出现失败,不必担心数据丢失。因为旧的基础文件和增量文件(如果有)加上新开启的增量文件,可以组成一个完整的数据集。因此,即便重写失败,Redis 也能保证数据的一致性。
  1. 生成清单文件并交换
  • 当子进程完成重写工作后,它会通知父进程,父进程将使用子进程生成的新的基础文件和增量文件来创建一个临时清单文件(manifest)。
  • 清单文件:清单文件记录了所有的 AOF 文件和它们的顺序。父进程完成清单文件后,会进行一次原子交换,将新的清单文件切换到 Redis 系统中生效。
  1. 清理旧文件
  • 一旦重写过程完成,Redis 会删除旧的基础文件和任何未使用的增量文件。这个操作保证了数据的一致性,并避免了存储空间的浪费。

Redis < 7.0 的 AOF 重写过程

  1. 创建子进程
    和 Redis >= 7.0 一样,Redis 会通过 fork 创建子进程和父进程。
  2. 子进程重写 AOF 文件
  • 子进程会开始写入新的 AOF 文件,该文件是一个全新的临时文件。
  • 父进程:父进程同时会将来自客户端的新写操作写入到内存中的缓冲区,并将这些操作直接追加到旧的 AOF 文件中。
  1. 增量数据缓冲
  • 父进程在重写过程中将新操作存入内存缓冲区,保证即使在重写过程中发生故障,已有的数据不会丢失,因为操作会被追加到旧的 AOF 文件中。
  1. 重写完成后合并
  • 当子进程完成重写后,它会通知父进程,父进程将把内存缓冲区中的数据追加到子进程重写的临时文件中。
  • 然后,Redis 会原子地将这个新文件重命名为旧文件,确保文件更新的完整性。
  1. 切换文件和继续写入
  • 重命名完成后,Redis 会继续将新的写入操作追加到新的 AOF 文件中,确保 AOF 文件的持久化。

  • Redis >= 7.0 版本中,采用了更先进的多文件结构(基础文件 + 增量文件 + 清单文件)来进行 AOF 重写,确保了更高的可用性和更精细的管理。

  • Redis < 7.0 版本中,重写过程相对简单,主要依赖于在重写过程中将新命令缓存在内存中,避免数据丢失。

5、配置文件

AOF相关配置内容在 conf中的 APPEND ONLY MODE下。

在这里插入图片描述

在 Redis 配置文件中,关于 AOF (Append Only File) 的设置有一系列的配置项,它们影响 Redis 数据持久化的行为。我们可以从文件中的几个关键部分进行分析,帮助理解各个配置的含义及其作用。

  1. appendonly

在这里插入图片描述

appendonly no

作用:此配置项控制是否启用 AOF 持久化。

解释:当 appendonly 设置为 yes 时,Redis 会将每次写操作以命令的方式记录到 AOF 文件中,从而实现持久化。此配置为 no 时,表示禁用 AOF 持久化,Redis 仅依赖 RDB 快照进行数据持久化。

  1. appendfilename

在这里插入图片描述

appendfilename "appendonly.aof"

作用:指定 AOF 文件的文件名。

解释:Redis 会将所有写操作追加到此文件中。可以根据需要修改此文件名,或者将文件路径更改为其他位置。如果没有指定完整路径,默认会保存在 dir 配置指定的目录中。

  1. appenddirname

在这里插入图片描述

appenddirname "appendonlydir"

作用:指定存储 AOF 文件的目录。

解释:这是一个便捷配置,你将所有的 AOF 文件集中存储在一个指定的目录中。通过修改此项,可以更改 AOF 文件存储位置。

  1. appendfsync

在这里插入图片描述

appendfsync everysec

作用:控制 fsync() 操作的频率,以确保数据持久化到磁盘。

解释

  • no:不调用 fsync(),由操作系统决定何时将数据刷写到磁盘。
  • always:每次写操作后都调用 fsync(),最安全但最慢。
  • everysec:每秒钟调用一次 fsync(),这是性能与安全性的折衷选择,默认配置。
  1. no-appendfsync-on-rewrite
no-appendfsync-on-rewrite no

作用:在 AOF 重写期间,控制是否停止 fsync() 调用。

解释:当设置为 yes 时,在 BGSAVEBGREWRITEAOF 操作期间,不会调用 fsync(),以避免 I/O 阻塞。但这意味着在此期间,数据的持久化保证会变得不那么严格。默认情况下为 no,这意味着即使在重写期间也会进行 fsync(),确保持久化的可靠性。

  1. auto-aof-rewrite-percentage
auto-aof-rewrite-percentage 100

作用:配置自动重写 AOF 文件的触发条件。

解释:Redis 会根据这个百分比值与当前 AOF 文件大小比较,决定是否触发 AOF 重写。当当前 AOF 文件的大小比上次重写后大于此百分比时,Redis 会自动进行重写。100 表示当文件大小增长一倍时,触发重写。

  1. auto-aof-rewrite-min-size
auto-aof-rewrite-min-size 64mb

作用:配置自动重写 AOF 文件时,要求文件的最小大小。

解释:如果 AOF 文件小于此大小,即使超过了 auto-aof-rewrite-percentage 的触发条件,Redis 也不会自动重写 AOF 文件。此配置避免了在小文件情况下频繁重写,通常用于提高性能。

  1. aof-load-truncated
aof-load-truncated yes

作用:控制当 AOF 文件在 Redis 启动时被发现被截断时的行为。

解释:如果设置为 yes,即使 AOF 文件在尾部被截断,Redis 仍然会尽可能加载文件,并从文件中恢复尽可能多的数据。如果设置为 no,Redis 启动时会因为文件损坏而终止,用户需要修复文件后才能重新启动。

  1. aof-use-rdb-preamble
aof-use-rdb-preamble yes

作用:控制是否在 AOF 文件的开头包含 RDB 快照。

解释:如果设置为 yes,Redis 会在 AOF 文件的开头包含一个完整的 RDB 快照,作为数据的初始状态。这意味着,Redis 在恢复时既可以加载 AOF 文件,也可以使用这个 RDB 快照来还原数据库。如果设置为 no,Redis 不会在 AOF 文件中包含 RDB 快照,而是直接从 AOF 文件的增量数据开始恢复。

  1. aof-timestamp-enabled
aof-timestamp-enabled no

作用:控制是否在 AOF 文件中记录时间戳。

解释:如果设置为 yes,Redis 会在每个 AOF 命令前添加时间戳,用于精确恢复到某一时刻的数据。这会改变 AOF 文件的格式,因此如果有旧版本的 AOF 解析器,可能会导致兼容性问题。默认情况下为 no,即不记录时间戳。

这些配置项为 Redis 的 AOF 持久化提供了灵活的控制,允许用户根据不同的需求在性能和数据安全性之间做权衡。常见的配置策略如下:

  • 安全性优先appendfsync alwaysauto-aof-rewrite-min-sizeaof-use-rdb-preamble yes 都可以增强数据持久化的安全性,但会牺牲一些性能。
  • 性能优先appendfsync noeverysec 可以提高性能,但可能会丢失少量的数据。
  • 容灾恢复aof-load-truncated yesno-appendfsync-on-rewrite yes 可以在发生灾难时减少数据丢失或避免 Redis 启动失败。

配置文件中的这些设置,允许你精确地控制 Redis 的持久化策略,从而在高并发、高可用场景下保持系统的稳定性和数据的安全性。

6、AOF的优势与劣势

AOF的优点

优点:更好的保护数据不丢失、性能高、可做紧急恢复

  1. 更高的持久化性:使用AOF,Redis提供了更强的持久化保障。可以选择不同的fsync策略:不执行fsync、每秒执行一次fsync、每个查询都执行fsync
    • 默认的fsync策略是每秒一次,这样写性能依然非常高。fsync操作是在后台线程执行的,主线程会尽量在没有fsync操作时进行写入,因此最多只会丢失一秒钟内的写入。
  2. 追加式日志:AOF日志是追加式的,这意味着没有文件寻址(seek)操作,也没有因为断电而导致的文件损坏问题。即使在写入日志过程中(如磁盘已满或其他原因)命令未完全写入,redis-check-aof工具也能轻松修复该日志。
  3. 自动重写AOF文件:当AOF文件变得过大时,Redis可以在后台自动重写AOF文件。重写过程是安全的,在重写过程中,Redis会继续向旧的AOF文件追加日志,同时会创建一个包含当前数据集所需最小操作的新文件。一旦新文件准备好,Redis会切换到新的文件并开始继续写入。
  4. 易于理解和导出的日志格式:AOF文件记录了所有操作,按顺序排列,格式简单易懂。即使发生了类似FLUSHALL命令的误操作,只要在重写日志之前没有其他操作,停止服务器、移除最新的命令并重启Redis,就可以恢复数据集。

AOF的缺点

  1. AOF文件较大:与RDB文件相比,AOF文件通常会更大,存储相同的数据集时,AOF文件的大小通常会超过RDB文件。恢复速度鳗鱼 RDB。
  2. 性能较低:根据不同的fsync策略,AOF可能会比RDB稍慢。一般来说,当fsync设置为每秒一次时,性能仍然很高;如果禁用fsync,在高负载下AOF性能应该和RDB一样快。然而,RDB在高写负载下能提供更稳定的最大延迟保障。
  3. 内存使用(Redis 7.0之前):如果在AOF重写过程中有写入操作,这些写入会在内存中缓冲,并在重写结束后一起写入新的AOF文件。所有在重写期间到达的写操作都会被写入两次:一次到旧的AOF文件,另一次到新的AOF文件。
  4. 重写时可能引发性能问题:在AOF重写期间,Redis可能会暂停写操作和fsync操作,导致暂时的性能下降,尤其是在重写过程中大量写操作到达时。
  • (根据确切的fsync策略,AOF运行效率可能要慢于RDB。一般来说,将fsync设置为每秒性能依然非常高,并且在禁用fsync的情况下,即使在高负载下它也应该与RDB一样快,即使在巨大的写入负载的情况下,RDB仍然能够提供关于最大延迟的更多保证。)AOF运行效率要慢于RDB,每秒同步策略效率较好,不同步效率和RDB相同。

与 RDB 持久化相比,AOF 持久化的实时性更好。AOF 持久化的fsync策略为 no、everysec 时都会存在数据丢失的情况 。always 下可以基本是可以满足持久性要求的,但性能太差,实际开发过程中不会使用。


四、RDB 和 AOF混合持久化

1、混合持久化的概念

由于 RDB 和 AOF 各有优势,于是,Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。

混合持久化旨在结合 RDB 和 AOF 两种方式的优点,为用户提供更高的性能和更强的数据安全性:

  1. RDB 的优点
    • 快速加载:RDB 文件是二进制格式,适合大规模数据的快速加载。
    • 写入效率高:RDB 生成的是一个全量快照,写入频率低,不会对 Redis 性能造成显著影响。
  2. AOF 的优点
    • 更小的潜在数据丢失:AOF 记录每条命令,具有更高的实时性,能够最大程度地减少宕机后丢失的数据量。
    • 可读性强:AOF 文件是纯文本格式,可以直接查看和编辑。
  3. 混合持久化的核心目标
    • 在 AOF 文件中存储 RDB 的快照数据(即二进制数据)和追加的增量命令。这样既可以利用 RDB 的快速加载能力,也可以利用 AOF 的高实时性。

如果想要开启混合持久化功能,可以在 Redis 配置文件将下面这个配置项设置成 yes:

aof-use-rdb-preamble yes
  • 设置为 yes 时,Redis 在进行 AOF 重写时会将 RDB 数据写入文件开头;
  • 设置为 no 时,仅使用传统 AOF 追加的方式。

同时也需要开启 AOF 和 RDB。例如:

save 900 1
save 300 10
appendonly yes
aof-use-rdb-preamble yes

如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。

那 AOF 和 RDB 两个可否共存?如果共存听谁的?

在这里插入图片描述

Redis配置文档解答:RDB和AOF共存时会优先加载AOF文件

同时开启两种持久化方式

  • 在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。
  • RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。但是作者也不建议只使用AOF方式备份,因为RDB更适合用于备份数据库(AOF在不断的变化不好备份),留着RDB作为一个万一的手段。

2、工作原理

  1. 触发持久化:当 Redis 触发 AOF 重写(手动或自动)时,混合持久化将发挥作用。
  2. RDB 快照写入:在重写过程中,Redis 首先将当前内存数据以 RDB 格式写入 AOF 文件的开头。这部分数据是二进制的,表示快照状态。
  3. 追加增量命令:在 RDB 快照写入完成后,Redis 会将重写期间产生的增量命令以 AOF 格式追加到文件中。
  4. 混合持久化完成:最终生成的 AOF 文件既包含 RDB 的快照数据,也包含增量的命令。这使得文件既能快速恢复大规模数据,又能保证较小的宕机数据丢失量。

RDB镜像做全量持久化,AOF做增量持久化先使用RDB进行快照存储,然后使用AOF持久化记录所有的写操作,当重写策略满足或手动触发重写的时候,将最新的数据存储为新的RDB记录。这样的话,重启服务的时候会从 RDB 和 AOF 两部分恢复数据,既保证了数据完整性,又提高了恢复数据的性能。简单来说:混合持久化方式产生的文件一部分是 RDB 格式,一部分是 AOF 格式。也就是说,AOF包括了RDB头部+AOF混写。

这样的好处在于,重启 Redis 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快。加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失

面试题:数据恢复顺序和加载流程

当 Redis 同时启用 RDB 和 AOF 时,服务器启动时会优先根据以下规则选择合适的持久化文件进行数据恢复:

  1. 优先加载 AOF 文件:Redis 在启动时会首先检查是否存在 AOF 文件。如果存在,Redis 将根据 AOF 文件恢复数据。

    • 原因:AOF 文件记录了更多的实时命令,数据更加完整。

    • 加载过程:

      • 如果开启了混合持久化,Redis 会先加载 AOF 文件中的 RDB 部分,然后执行增量命令。
      • 如果 AOF 文件受损,Redis 会尝试修复(通过 redis-check-aof 工具)。
  2. 备选加载 RDB 文件:如果 AOF 文件不存在或无法加载,Redis 会尝试使用 RDB 文件恢复数据。

    • 优势:RDB 文件提供了完整的快照,恢复速度快。

    • 缺点:RDB 可能丢失最近一次快照后的数据。

  3. 无持久化文件:如果 AOF 和 RDB 文件都不存在,Redis 将以空数据集启动。


五、纯缓存模式

同时关闭RDB+AOF,专心做缓存

  1. save "" – 禁用RDB

    禁用RDB持久化模式下,我们仍然可以使用命令savebgsave生成RDB文件

  2. appendonly no – 禁用AOF

    禁用AOF持久化模式下,我们仍然可以使用命令bgrewriteaof生成AOF文件。


六、持久化的选择与策略

怎么选?用哪个?

  • RDB持久化方式能够在指定的时间间隔对你的数据进行快照存储。
  • AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以 Redis 协议追加保存每次写的操作到文件末尾。

RDB 比 AOF 优秀的地方

  • RDB 文件存储的内容是经过压缩的二进制数据, 保存着某个时间点的数据集,文件很小,适合做数据的备份,灾难恢复。AOF 文件存储的是每一次写命令,类似于 MySQL 的 binlog 日志,通常会比 RDB 文件大很多。当 AOF 变得太大时,Redis 能够在后台自动重写 AOF。新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。不过, Redis 7.0 版本之前,如果在重写期间有写入命令,AOF 可能会使用大量内存,重写期间到达的所有写入命令都会写入磁盘两次。
  • 使用 RDB 文件恢复数据,直接解析还原数据即可,不需要一条一条地执行命令,速度非常快。而 AOF 则需要依次执行每个写命令,速度非常慢。也就是说,与 AOF 相比,恢复大数据集的时候,RDB 速度更快。

AOF 比 RDB 优秀的地方

  • RDB 的数据安全性不如 AOF,没有办法实时或者秒级持久化数据。生成 RDB 文件的过程是比较繁重的, 虽然 BGSAVE 子进程写入 RDB 文件的工作不会阻塞主线程,但会对机器的 CPU 资源和内存资源产生影响,严重的情况下甚至会直接把 Redis 服务干宕机。AOF 支持秒级数据丢失(取决 fsync 策略,如果是 everysec,最多丢失 1 秒的数据),仅仅是追加命令到 AOF 文件,操作轻量。
  • RDB 文件是以特定的二进制格式保存的,并且在 Redis 版本演进中有多个版本的 RDB,所以存在老版本的 Redis 服务不兼容新版本的 RDB 格式的问题。
  • AOF 以一种易于理解和解析的格式包含所有操作的日志。可以轻松地导出 AOF 文件进行分析,也可以直接操作 AOF 文件来解决一些问题。比如,如果执行FLUSHALL命令意外地刷新了所有内容后,只要 AOF 文件没有被重写,删除最新命令并重启即可恢复之前的状态。

综上

  • Redis 保存的数据丢失一些也没什么影响的话,可以选择使用 RDB。
  • 不建议单独使用 AOF,因为时不时地创建一个 RDB 快照可以进行数据库备份、更快的重启以及解决 AOF 引擎错误。
  • 如果保存的数据要求安全性比较高的话,建议同时开启 RDB 和 AOF 持久化或者开启 RDB 和 AOF 混合持久化。

七、恢复与故障恢复

1、AOF 被截断或损坏

当 Redis 的 AOF 文件发生截断或损坏时,需要采取一定的步骤来恢复数据或修复文件。下面是针对 AOF 截断AOF 损坏 的处理步骤的总结与分析。

  1. AOF 截断的处理

AOF 文件截断通常发生在 Redis 写入数据到 AOF 文件的过程中,可能由于以下原因:

  • Redis 服务器崩溃。
  • 存储 AOF 文件的磁盘空间已满。
  • 磁盘 I/O 问题或文件系统问题导致数据写入中断。

Redis 默认行为:在 Redis 启动时,如果发现 AOF 文件的尾部被截断,且配置了 aof-load-truncated yes,Redis 会继续加载 AOF 文件,并忽略最后一条未完全写入的命令。此时,Redis 会发出警告信息:

# !!! Warning: short read while loading the AOF file !!!
# !!! Truncating the AOF at offset 439 !!!
# AOF loaded anyway because aof-load-truncated is enabled

这个警告表示文件的尾部被截断,但 Redis 会继续加载文件并启动,保证系统可用性。

如果想要强制 Redis 在 AOF 截断时停止,可以修改配置 aof-load-truncatedno,这样 Redis 在发现 AOF 截断时会停止启动,要求用户修复文件后才能重新启动。

  • 如果使用的是较早版本的 Redis,可能无法自动恢复 AOF 文件,解决步骤如下:

    1. 备份 AOF 文件:在操作前最好先备份 AOF 文件。

    2. 使用 redis-check-aof 工具修复文件:可以使用 Redis 提供的

      redis-check-aof
      

      工具来修复 AOF 文件。命令如下:

      redis-check-aof --fix <filename>
      

      该工具会尝试修复 AOF 文件,删除损坏的部分,并尽可能恢复数据。

    3. 检查修复结果:可以通过 diff -u 命令比较修复后的文件和原始文件,查看修复过程中丢失的部分。

    4. 重新启动 Redis:修复完成后,重新启动 Redis 服务器,使用修复后的 AOF 文件。

风险:如果 redis-check-aof 工具修复文件时,会丢弃从损坏部分到文件末尾的数据,这可能会导致部分数据丢失,尤其是在 AOF 文件的初始部分发生损坏时,丢失的数据可能会比较多。

  1. AOF 文件损坏的处理

AOF 文件损坏通常是由于文件内部的字节序列无效,可能发生在文件的任意位置,导致 Redis 在启动时无法加载 AOF 文件。

当 Redis 启动时检测到 AOF 文件损坏,错误日志可能显示如下内容:

# Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>

这种情况下,Redis 会因为 AOF 文件损坏而终止启动。

备份文件:首先,备份 AOF 文件,以防止修复过程中进一步损坏。

  • 使用 redis-check-aof 工具:
    • 初步运行 redis-check-aof 工具时,不要加 --fix 选项,这样工具会分析文件,查找损坏的部分。通过查看输出日志和工具给出的文件偏移量信息,可以定位文件损坏的位置。
    • 使用工具提供的偏移量信息,手动修复文件。如果你熟悉 Redis 协议格式,可以手动修复损坏的部分,因为 AOF 文件使用 Redis 协议格式,修复相对简单。
  • 自动修复:如果无法手动修复文件,可以使用 --fix 选项来让 redis-check-aof 自动修复文件。此时,工具会丢弃从损坏部分到文件末尾的所有内容,因此数据丢失是不可避免的。

AOF 校验机制是 Redis 在启动时对 AOF 文件进行检查,以判断文件是否完整,是否有损坏或者丢失的数据。这个机制的原理其实非常简单,就是通过使用一种叫做 校验和(checksum) 的数字来验证 AOF 文件。这个校验和是通过对整个 AOF 文件内容进行 CRC64 算法计算得出的数字。如果文件内容发生了变化,那么校验和也会随之改变。因此,Redis 在启动时会比较计算出的校验和与文件末尾保存的校验和(计算的时候会把最后一行保存校验和的内容给忽略点),从而判断 AOF 文件是否完整。如果发现文件有问题,Redis 就会拒绝启动并提供相应的错误信息。AOF 校验机制十分简单有效,可以提高 Redis 数据的可靠性。类似地,RDB 文件也有类似的校验机制来保证 RDB 文件的正确性。

2、RBD 文件损坏

若rbd文件倍损坏,如何检查修复dump.rdb文件?

Redis提供了一个工具redis-check-rdb,它可以帮助检查和修复损坏的RDB文件。你可以使用以下命令来检查和修复RDB文件:

redis-check-rdb dump.rdb

如果redis-check-rdb工具能修复该文件,它会提供修复后的版本。虽然它无法保证100%的成功率,但在某些情况下可以恢复部分数据。

[root@study bin]# pwd
/usr/local/bin
[root@study bin]# ls -la
total 29228
drwxr-xr-x.  2 root root     4096 Nov 21 20:20 .
drwxr-xr-x. 17 root root     4096 Nov 21 18:33 ..
-rwxr-xr-x   1 root root  7372392 Nov 21 20:20 redis-benchmark
lrwxrwxrwx   1 root root       12 Nov 21 20:20 redis-check-aof -> redis-server
lrwxrwxrwx   1 root root       12 Nov 21 20:20 redis-check-rdb -> redis-server
-rwxr-xr-x   1 root root  7664000 Nov 21 20:20 redis-cli
lrwxrwxrwx   1 root root       12 Nov 21 20:20 redis-sentinel -> redis-server
-rwxr-xr-x   1 root root 14877528 Nov 21 20:20 redis-server

内容参考:https://redis.io/docs/latest/operate/oss_and_stack/management/persistence/

;