Redis 10_Redis持久化

一、Redis持久化简介

1.1 Redis持久化

Redis是一个基于内存的数据库,它的数据是存放在内存中,内存中的数据有个问题就 服务进程重启、意外崩溃 或者 OS系统宕机重启、断电重启等情况下会丢失。为了确保数据的持久性和可靠性,Redis引入了 持久化机制,允许将数据定期保存到磁盘中,以便在下次启动 Redis服务进程 时能够恢复原来的数据到内存中。

Redis 的持久化机制是 Redis 数据库的一个重要组成部分,它允许将内存中的数据以不同的方式写入磁盘,以防止数据丢失。这对于许多应用场景非常关键,特别是需要长期保存数据或具有高可用性要求的系统。

Redis提供了2种不同形式的持久化机制:

  • RDB(Redis DataBase):简而言之,就是在指定的时间间隔内,定时的将 redis 存储的数据生成 Snapshot快照 并存储到磁盘等介质上,在下次 redis 重新启动时,将快照加载到内存,就可以实现数据恢复了(该方式会丢失快照生成后到redis 进程停止这段期间的数据);
  • AOF(Append Of File):将 redis 执行过的所有写指令记录下来,在下次 redis 重新启动时,只要把这些写指令从前到后按顺序再重复执行(重放)一遍,就可以实现数据恢复了;

Redis 还支持 结合了 RDB 和 AOF 的 混合持久化 方式,此方式充分利用了 RDB 和 AOF 各自的优势,提高数据的可靠性,在这种情况下,如果 redis 重启的话,则会优先采用 AOF 方式来进行数据恢复,这是因为 AOF 方式的数据恢复完整度更高。

需要说明的是,Redis 的持久化 会对其服务(处理客户端请求)产生影响,如果没有数据持久化的需求,也完全可以关闭 RDB 和 AOF 方式持久化,这样的话,redis 将变成一个纯内存数据库(性能更好),就像 memcache 一样。

二、RDB(Redis DataBase)持久化

2.1 RDB快照的概念

RDB(Redis DataBase) 是 Redis 的默认持久化方式,它用于将当前内存中的数据保存到硬盘上的一个快照(snapshot)文件中。这个快照文件包含了一个特定时刻的所有数据,包括键值对、数据结构等。RDB 的工作方式类似于数据库的备份,它将内存中的数据定期保存到一个持久化文件中,以便在需要时进行数据恢复。

RDB 持久化机制的优点

  • 高性能:RDB持久化机制在性能上表现出色。因为RDB是通过 fork 子进程来完成的,主进程不需要执行繁重的 IO 操作,这可以确保Redis 在生成 RDB 快照时保持高吞吐量。
  • 紧凑和可读性好:RDB 文件采用了一种高效的二进制格式,可以很好地保存数据,并且非常紧凑。这使得 RDB 文件既节省磁盘空间,又具有很高的可读性,方便备份和迁移。
  • 适用于灾难恢复:RDB 文件是完整的数据库快照,可以用于从灾难中恢复数据,例如硬盘故障或不可逆的数据损坏。
  • 备份和迁移:由于 RDB 文件的紧凑性和可读性,它非常适用于备份数据或在不同环境之间迁移 Redis 数据。
  • 节省磁盘空间:可以根据需要配置 RDB 的保存频率,从而在一定程度上控制磁盘空间的占用。

RDB 持久化机制的缺点

  • 数据可能有损失:RDB 是定期生成的快照文件,如果 Redis 服务器崩溃,可能会丢失从最后一次生成快照到之后的数据。
  • 不适用于大规模数据:在处理大规模数据时,生成 RDB 文件可能会导致较长时间的阻塞,影响性能。在某些情况下,阻塞时间可能会变得不可接受。
  • 不适用于实时持久化需求:RDB 是基于快照的,因此不能提供实时持久化。如果需要每个写操作都立即持久化到磁盘,RDB 可能不是最佳选择。
  • 配置文件的修改:如果频繁地修改Redis的配置文件,RDB可能就变得不适合,因为它只有在 Redis 服务器正常关闭时才会生成快照。这可能会导致在配置更改后发生数据丢失。

总之,RDB 持久化机制在性能、备份和紧凑性方面表现出色,适用于许多使用场景,但需要注意数据可能会有损失,并且在处理大规模数据时需要慎重考虑。如果对实时持久化有更高要求,可以考虑使用 AOF 持久化机制或混合持久化来弥补 RDB 的不足。

2.2 RDB快照的原理

Redis 是单线程程序,这个线程要同时负责多个客户端套接字的并发读写操作和内存数据结构的逻辑读写。

Redis在开启 RDB快照持久化的情况下,在服务线上请求的同时,还需要进行内存快照,内存快照要进行文件 IO 操作,这意味着单线程同时在服务线上的请求还要进行文件 IO 操作,文件 IO 操作会严重拖垮服务器请求的性能。还有个重要的问题是为了不阻塞线上的业务,就需要边持久化边响应客户端请求, 因此,持久化的同时,可能内存数据结构还在改变,比如一个大型的 hash 字典正在持久化,结果一个请求过来把它给修改 或 删掉了?

为了解决这类问题,Redis 使用操作系统的多进程 COW(Copy On Write)写入时复制 机制来实现快照持久化。

Tips: COW(Copy On Write) 写入时复制 核心思想是,如果有多个调用者(callers)同时请求相同资源(如内存或磁盘上的数据存储),它们会获取相同的指针(内存地址)指向相同的(共享)资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者(修改),而其它调用者所见到的最初的资源仍然保持不变,这过程对其它的调用者都是 [透明]的。此做法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源。

Redis 在持久化时会调用 glibc 的 fork函数 产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段(这是 Linux 操作系统提供的COW机制,为了节约内存资源,所以尽可能让新创建的子进程共享父进程的内存资源,在刚创建进程的一段时间内,Redis 内存的增长几乎没有明显变化。)

Redis 子进程做数据持久化操作,不会修改现有的内存数据结构,只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改。

这个时候就会使用操作系统的 写入时复制(Copy On Write) 机制来进行数据段页面的分离。数据段是由很多操作系统的页面(每个页面大小为 4K)组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据。

随着父进程修改操作的持续进行,越来越多的共享页面被分离出来,这个过程内存就会持续增长。但是也不会超过原有数据内存的 2 倍大小。另外,一个 Redis 实例里冷数据占的比例往往是比较高的,所以很少会出现所有的页面都会被分离,被分离的往往只有其中一部分页面。每个页面的大小只有 4K,一个 Redis 实例里面一般都会有成千上万的页面。

子进程因为数据没有变化,它能看到的内存里的数据在进程产生的一瞬间就凝固了,再也不会改变,这也是为什么 Redis 的持久化叫「快照」的原因。接下来子进程就可以非常安心的遍历数据进行序列化写磁盘了。

在子进程完成持久化操作以后,需要通知父进程,父进程要做响应记录,保留最后一次持久化的时间。 相关;流程如下图:

2.3 RDB 的相关配置

Redis的RDB持久化可以通过在Redis配置文件(通常是redis.conf)中进行相关配置。

Tips: 注意,要使配置更改生效,就需要重启 Redis 服务器。根据具体的应用需求和数据量,可以调整这些配置项,以满足性能和持久性的要求。

以下是一些与RDB持久化有关的常见配置项: 1、配置RDB文件名和路径

1
2
dbfilename dump.rdb    # 指定RDB快照文件的名称(默认为 dump.rdb)
dir /var/lib/redis     # 指定RDB文件的保存路径(默认为 redis进程的工作目录 ./)

这些配置项允许指定 RDB 快照文件的名称和保存路径。注意需要确保文件夹存在并具有适当的权限。

2、保存快照的频率(触发RDB备份): 配置自动备份规则

1
2
3
save 900 1        # 表示在900秒(15分钟)内,如果至少有1个键发生变化,就会触发RDB快照保存。
save 300 10       # 表示在300秒(5分钟)内,如果至少有10个键发生变化,就会触发RDB快照保存。
save 60 10000     # 表示在60秒内,如果至少有10000个键发生变化,就会触发RDB快照保存。

save 命令用来配置备份的规则,save的格式: save 秒钟 写操作次数 默认是1分钟内修改了1万次,或5分钟内需修改了10次,或30分钟内修改了1次。

这些配置项定义了 RDB 持久化的触发条件,根据具体的应用需求,就可以根据不同的时间间隔和键变化数来触发 RDB 快照。

3、禁用 RDB 自动持久化: 如果想禁用自动触发的 RDB 持久化,可以将save配置项设置为空字符串。

1)通过Redis配置文件关闭

1
save ""              # 禁用自动RDB持久化 (默认情况下是开启的)

2)使用命令关闭 RDB 持久化:

1
2
127.0.0.1:6379> config set save ""
OK

4、RDB 持久化压缩

1
rdbcompression yes  # 开启 RDB 文件压缩(默认情况下是开启的)

这个配置项控制是否对生成的RDB文件进行压缩,开启后可以减小 RDB 文件的大小,但会占用额外的 CPU 资源。

5、校验和检查RDB文件

1
rdbchecksum yes     # 在保存RDB文件时是否进行校验和检查(默认情况下是开启的)

这个配置项控制是否在保存 RDB 文件时进行校验和检查,以确保文件的完整性。

2.4 RDB 持久化的触发时机

Redis RDB持久化的触发时机分为自动触发和手动触发两种方式: RDB持久化在四种情况下会执行:

  • 执行 SAVE 命令
  • 执行 BGSAVE 命令
  • Redis停机时
  • 触发RDB条件时

1、自动触发RDB持久化 自动触发是通过配置文件中配置的保存快照的频率来实现的。在 Redis 的配置文件(redis.conf)中,可以设置一个或多个 SAVE 指令来定义何时自动触发 RDB 快照的保存。每个 SAVE 指令都有两个参数,第一个参数是时间间隔(单位为秒),第二个参数是发生变化的键的数量。

这些配置项定义了 RDB 自动触发快照保存的条件。Redis 会定期检查这些条件,如果满足其中任何一个条件,就会触发RDB快照的生成(执行BGSAVE)。如果配置了save “",则会禁止自动触发

2、Redis 停机时触发RDB持久化 Redis停机时会执行一次 SAVE 命令,实现RDB持久化。

3、手动触发RDB持久化:SAVE 和 BGSAVE 手动触发 RDB持久化快照是通过 Redis 的命令来实现的,主要有两个命令可用:

  • SAVE命令: 使用 SAVE 命令可以立即生成一个 RDB 快照,它由Redis主进程来执行RDB,会阻塞 Redis 服务,直到 RDB 快照生成完成为止。这个命令不常用,因为它会导致 Redis 服务器在生成快照期间停止响应其它请求;只有在数据迁移时可能用到;
  • BGSAVE命令: 使用 BGSAVE 命令可以在后台生成 RDB 快照,而不会阻塞 Redis 服务器的正常操作,这是通常推荐的手动触发方式;

BGSAVE 命令 会使用操作系统提供的 fork 系统调用启动一个子进程,该子进程负责生成 RDB 快照文件,而主进程继续响应其它请求,一旦生成完成,BGSAVE 会将 RDB 文件保存到磁盘,然后通知主进程。

BGSAVE 命令的运作流程: BGSAVE 命令说明:

  • 执行 BGSAVE 命令时,Redis 父进程首先会判断当前进程是否存在其它正在执行的子进程,例如执行 RDB或者AOF 相关命令的子进程,如果存在,此时的 BGSAVE 命令则会直接返回。
  • 父进程会执行 fork 创建子进程,fork 的过程中父进程会阻塞,通过 info stats 命令查看 latest_fork_usec 选项,可以获取最近一次 fork操作的耗时,单位为微秒
  • 父进程执行 fork 完成后,BGSAVE 命令会返回 “Background saving started” 信息,此后将不再阻塞父进程,可以继续响应其它连接、执行命令,同时子进程负责生成 RDB 快照文件。
  • 子进程在创建 RDB 文件时,会根据父进程的内存生成临时的快照文件,完成后会对原有dump.rdb文件进行原子替换。执行 lastsave 命令可以获取最后⼀次生成 RDB 的时间,对应 info 统计的 rdb_last_save_time 选项。
  • 子进程创建RDB文件完成后会发送信号给父进程表示完成了,父进程则会更新统计信息。

SAVE 和 BGSAVE 命令的区别:

  • SAVE不用创建新的进程,速度略快,但会阻塞Redis 服务(直至完成持久化)
  • BGSAVE需要创建子进程,消耗额外的内存,基本不会阻塞Redis 服务(创建子进程时毫秒级短暂阻塞)
  • SAVE适合停机维护,在服务低谷时段操作
  • BGSAVE适合线上执行

2.5 RDB 快照文件的处理

RDB文件是 Redis 数据库的快照文件,它保存了当前内存中的数据以便在需要时进行恢复。以下是关于RDB文件的处理和管理的重要信息: 1、保存 RDB 文件 文件保存路径:RDB 文件保存在 Redis 配置文件中指定的目录下,默认情况下是/var/lib/redis/。文件名由配置文件中的dbfilename参数指定,默认为 “dump.rdb”。可以在 Redis 运行时使用 CONFIG SET 命令动态更改保存目录和文件名,例如:

1
2
127.0.0.1:6379> CONFIG SET dir /new/directory/
127.0.0.1:6379> CONFIG SET dbfilename newdump.rdb

这将使RDB文件保存到新的目录和使用新的文件名。

手动保存: 可以通过执行SAVEBGSAVE命令来手动触发 RDB 文件的保存。SAVE命令会在 Redis 服务器生成 RDB 文件的同时阻塞其它请求,而 BGSAVE 命令会在后台生成 RDB 文件,不会阻塞其它操作。

1
2
127.0.0.1:6379> SAVE
127.0.0.1:6379> BGSAVE

2、压缩 RDB 文件 Redis默认采用 LZF 算法对生成的 RDB 文件进行压缩处理,以减小文件的体积,节省磁盘空间和网络带宽。压缩是默认开启的,但可以通过以下命令动态修改:

1
2
127.0.0.1:6379> CONFIG SET rdbcompression yes   # 开启RDB文件压缩
127.0.0.1:6379> CONFIG SET rdbcompression no    # 禁用RDB文件压缩

虽然压缩RDB文件会消耗CPU资源,但通常建议开启,特别是在磁盘空间和网络带宽有限的情况下,因为它可以显著减小文件的大小。

3、校验 RDB 文件 Redis 在启动时会加载 RDB 文件,如果文件损坏或格式错误,Redis 将拒绝启动。为了检查 RDB 文件的完整性和有效性,可以使用Redis提供的 redis-check-rdb 工具。这个工具会检测 RDB 文件并生成相应的错误报告,帮助识别和解决问题。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
root@~:/data# redis-check-rdb dump.rdb 
[offset 0] Checking RDB file dump.rdb
[offset 26] AUX FIELD redis-ver = '6.2.6'
[offset 40] AUX FIELD redis-bits = '64'
[offset 52] AUX FIELD ctime = '1703284567'
[offset 67] AUX FIELD used-mem = '811544'
[offset 83] AUX FIELD AOF-preamble = '0'
[offset 85] Selecting DB ID 0
[offset 936] Checksum OK
[offset 936] \o/ RDB looks OK! \o/
[info] 6 keys read
[info] 0 expires
[info] 0 already expired

处理和管理RDB文件是确保Redis数据持久性和可靠性的重要任务。了解如何保存、压缩和校验RDB文件可以帮助我们更好地管理 Redis 数据库。

三、AOF(Append Of File)持久化

3.1 AOF(Append Of File)的概念

AOF(Append-Only File) 是 Redis 的一种持久化机制,用于将 Redis 服务器的 写(添加、修改、删除)操作以追加的方式记录到一个文本文件中,从而实现数据持久化。每个写操作都会以 Redis 命令的形式追加到 AOF 文件的末尾,因此 AOF 文件是一个按照时间顺序记录写操作的日志文件。AOF 日志只记录对内存进行修改(添加、修改、删除)操作的指令记录。

Append only file(AOF)默认保存的文件名为 appendonly.aof。

假设 AOF 日志记录了自 Redis 实例创建以来所有的修改性指令序列,那么就可以通过对一个空的 Redis 实例顺序执行所有的指令,也就是命令 重放,来恢复 Redis 当前实例的内存数据结构的状态。

AOF 的优点

  • 数据安全性:AOF 记录了每个写操作,因此在服务器故障或崩溃时,只会丢失最后一次写操作之后的数据,这提供了较高的数据安全性;
  • 可读性:AOF 文件是一个文本文件,易于人类阅读和理解,这使得AOF文件在需要手动恢复数据或进行调试时非常有用;
  • 灵活性:AOF 文件的追加方式写入使得数据持久化非常实时,可以根据需求选择不同的同步策略,从完全同步到异步;
  • 自动重写:Redis 提供了 AOF 文件的自动重写机制,可以避免 AOF 文件过大,提高了性能;

AOF 的缺点

  • 文件体积:相对于 RDB 持久化,AOF 文件通常较大,因为它包含了每个写操作的命令文本。这可能会占用较多的磁盘空间;
  • 写入性能:AOF 持久化会导致每个写操作都需要追加到 AOF 文件中,这可能会对写入性能产生一定的影响,尤其是在使用同步写入策略时;

3.2 AOF(Append Of File)的原理

AOF 日志存储的是 Redis 服务器的顺序指令序列,AOF 日志只记录对内存进行修改的指令记录。在Redis重启时通过对一个空的 Redis 实例顺序执行(重放) AOF文件所有的指令,来恢复 Redis 新实例的内存数据结构的状态。

1)AOF(Append Of File)写入机制 Redis 在收到客户端修改命令后,先进行相应的校验,如果没问题,就立即将该命令存追加到 .aof 文件中,也就是先存到磁盘中,然后服务器再执行命令。这样就算遇到了突发的宕机情况情况,也只需将存储到 .aof 文件中的命令,进行一次 “命令重演” 就可以恢复到宕机前的状态。

在上述执行过程中,有一个很重要的环节就是命令的写入,这是一个 IO 操作。Redis 为了提升写入效率,不会将内容(命令)直接写入到磁盘中,而是将其放 AOF缓冲区(aof_buf)中,之后再由其它机制采用异步方式将缓存区中的内容真正写入到磁盘里。

这就意味着如果机器突然宕机,AOF缓冲区(aof_buf) 中的内容可能还没有来得及完全刷到磁盘中,这个时候就会出现数据丢失。

Redis 为数据的安全性考虑,同样为 AOF 持久化提供了策略配置,在Redis 配置文件中使用 appendfsync 命令来设置 AOF 持久化配置策略。

appendfsync 配置命令有以下三个参数指令:

  • Always:服务器每写入一个命令,就调用一次 fsync函数,将缓冲区里面的命令写入到硬盘。这种模式下,服务器出现故障,也不会丢失任何已经成功执行的命令数据,但是其执行速度较慢;
  • Everysec(默认):服务器每一秒调用一次 fsync 函数,将缓冲区里面的命令写入到硬盘。这种模式下,服务器出现故障,最多只丢失一秒钟内的执行的命令数据,通常都使用它作为 AOF 配置策略;
  • No:服务器不主动调用 fsync 函数,由操作系统决定何时将缓冲区里面的命令写入到硬盘。这种模式下,服务器遭遇意外停机时,丢失命令的数量是不确定的,所以这种策略,不确定性较大,不安全。

Tips: Linux 系统的 fsync() 函数可以将指定文件的内容从内核缓存刷到硬盘中。

由于 fsync 是磁盘 IO 操作,所以它很慢,如果 Redis 执行一条指令就要调用 fsync 一次(Always),那么 Redis 高性能将严重受到影响。

在生产环境的服务器中,Redis 通常是每隔 1s 左右执行一次 fsync 操作( Everysec),这样既保持了高性能,也让数据尽可能的少丢失。

最后一种策略(No),让操作系统来决定何时将数据同步到磁盘,这种策略存在许多不确定性,所以不建议使用。

AOF 写入是由 Redis 主线程负责执行的

2)AOF重写(rewrite)机制 Redis 在长期运行的过程中,AOF 文件会越变越长。如果机器宕机重启,“重演” 整个 AOF 文件会非常耗时,导致长时间 Redis 无法对外提供服务。因此就需要对 AOF 文件做一下 “瘦身” 运动。

为了让 AOF 文件的大小控制在合理的范围内,Redis 提供了 AOF重写(rewrite)机制,该机制可以压缩和优化 AOF 文件的内容,减少冗余和无效的命令,提高数据的存储效率和恢复速度 。

AOF 重写机制 是根据 Redis 数据库内存现状(这里面包含了数据库的最新数据)创建一个新的AOF文件,该机制读取数据库里的所有键值对,每一个键值对用一个命令来记入它的写入,新的AOF文件只包含当前有效、存在的数据(快照)的写入命令,而不是历史上所有的写入命令(流水)。

AOF 重写机制(的实现原理)创建了一个子进程对内存(快照)进行遍历转换成一系列 Redis 的操作指令,重写机制使用了Linux 写时复制(COW)技术,把Redis内存快照序列化到一个新的 AOF 日志文件中。序列化完毕后再将操作(重写)期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,这样就完成了AOF文件重写操作。

AOF 重写的流程:

  • AOF 重写机制是通过 fork 出一个子进程来完成的,子进程会扫描 Redis 的数据库内存快照(使用写时复制COW技术),并将内存中每个键值对转换为相应的写入命令,写入到一个临时AOF文件。
  • 在fork 出的子进程进行 AOF 重写的过程中,主进程还会继续接收和处理客户端的请求,如果有新的写操作发生,Redis主进程会将这些写操作 同时追加到 AOF缓冲区aof_buf(这样,即使重写区间宕机,旧的AOF流水文件的操作仍然是齐全的)和 AOF重写缓冲区 aof_rewrite_buf_blocks(这样,重写文件也不会丢失最新的操作) 中,并通过管道发送给子进程 。
  • 子进程在完成内存快照重写后,会读取父进程使用管道发来的新操作(写)命令数据并将其写入临时文件,然后向主进程发送信号,通知主进程可以切换到新的 AOF 文件了 。
  • 主进程在收到子进程的信号后,会再次把累计的差异数据写入临时AOF文件(以防止在此期间有新的写操作发生),并且对临时AOF文件rename,用它代替旧的AOF文件,至此就完成AOF的重写。

AOF 重写的执行流程如下:

  1. 执行 AOF 重写请求。
    • 如果当前进程正在执行 AOF 重写,请求不执行。
    • 如果当前进程正在执行 bgsave 操作,重写命令将延迟到 bgsave 完成后再执行。
  2. 父进程执行 fork 创建子进程。
  3. 重写过程: a. 主进程在 fork 之后继续响应其它命令。所有修改操作会被写入 AOF 缓冲区,并根据 appendfsync 策略同步到硬盘,以确保旧 AOF 文件机制的正确性。 b. 子进程只能获取到 fork 时刻的所有内存信息(快照),所以父进程需要将 fork 之后这段时间内的修改操作写入 AOF 重写缓冲区中。
  4. 子进程根据内存快照,将命令合并到新的 AOF 文件中。
  5. 子进程完成重写: a. 新文件写入后,子进程发送信号给父进程。 b. 父进程将 AOF 重写缓冲区内临时保存的命令追加到新 AOF 文件中。 c. 使用新 AOF 文件替换旧 AOF 文件。

AOF(Append Of File)有两个重要的缓冲区:AOF缓冲区(aof_buf)AOF重写缓冲区(aof_rewrite_buf_blocks)

  • AOF缓冲区(aof_buf) 是父进程将数据写入AOF前使用的缓冲区;
  • AOF重写缓冲区(aof_rewrite_buf_blocks) 是AOF重写期间,父进程用于存放差异数据的缓冲区;

为何重写的时候需要aof_rewrite_buf_blocks缓冲区 呢? 这是因为 fork操作使用的写时复制技术 COW(copy-on-write),子进程只能共享fork操作时的内存数据,对于之后的差异数据,主进程就单独开辟了一块缓冲区来保存,也就是 aof_rewrite_buf_blocks缓冲区

AOF 重写 过程是由后台子进程 BGREWRITEAOF 完成,以此避免阻塞主线程,但是数据库性能会下降。

Redies4.0 开始,重写是将 rdb的快照以二进制形式附在新aof头部,作为历史数据,替换原来流水账操作,之后再记录新产生的写操作流水。

重写操作一共发生 一个拷贝两处日志一次拷贝是内存快照数据拷贝,两处日志分别是旧的 AOF 日志和新的 AOF 重写日志。

AOF 重写机制可能遇到的问题: 1)AOF 重写机制可能会遇到内存不足或者内存碎片化的问题:

  • 内存不足是指当 fork 出子进程时,操作系统会为子进程分配和主进程相同大小的内存空间,如果主进程占用的内存过大,可能导致内存不足而 fork 失败 。
  • 内存碎片化是指当 fork 出子进程时,操作系统会使用写时复制(copy-on-write)的技术,只有当主进程修改了内存页时,才会为子进程复制一个新的内存页 。但是如果主进程使用了大页(huge page)的特性,那么每次复制的内存页也会很大,可能导致内存碎片化而 fork 失败 。

2)解决内存不足或者内存碎片化的问题的方法有以下几种:

  • 增加物理内存或者虚拟内存;
  • 优化 Redis 的数据结构和编码,减少内存占用;
  • 关闭大页(huge page)的特性,避免复制过大的内存页;
  • 使用 Redis 4.0 及以上版本,利用 rdb-preamble 特性,将 RDB 文件写入到 AOF 文件开头,以加快数据恢复速度;

3.3 AOF 的相关配置

AOF 记录命令流水配置: 在Redis中,可以通过配置文件或使用 CONFIG 命令来配置 AOF 持久化的相关参数。

1
2
3
4
appendonly no                   # 是否开启AOF,yes:开启,no:不开启,默认为no
appendfilename "appendonly.aof" # aof文件名称,默认为appendonly.aof
dir ./                          # aof文件所在目录,默认./,表示执行启动命令时所在的目录
appendfsync everysec

AOF 配置选项:

  • appendonly:用于启用或禁用AOF持久化。设置为 “yes” 表示启用AOF,设置为 “no” 表示禁用。默认情况下,AOF持久化是禁用的,需要手动配置启用。
  • ppendfilename:指定AOF文件的文件名,默认为"appendonly.aof”。可以根据需要更改文件名。
  • appendfsync:指定AOF文件同步到磁盘的策略。可以选择的选项包括"always"(每次写操作都同步)、“everysec”(每秒同步一次)和"no"(完全异步)。

使用命令关闭 AOF 和 混合持久化:

1
2
127.0.0.1:6379> config set appendonly no
OK

AOF 触发重写机制方式和配置: 1)在客户端手动执行(向服务器发送)BGREWRITEAOF 命令,可以触发重写 AOF 文件机制,如下所示:

1
2
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started

2)配置文件中的选项,自动执行BGREWRITEAOF命令

1
2
3
# 默认配置项
auto-aof-rewrite-min-size 64mb  # 表示触发AOF重写的最小文件体积,大于或等于64MB自动触发
auto-aof-rewrite-percentage 100 # 只有当AOF文件的增量大于起始size的100%时,才自动触发

例如,如果将auto-aof-rewrite-min-size设置为64MB,auto-aof-rewrite-percentage设置为100%,那么只有当AOF文件的大小超过64MB,并且比上次重写后增长了100%或更多时,自动重写才会被触发。

  • auto-aof-rewrite-min-size < size > 参数表示当 当前AOF文件 大小超过指定值时,才可能触发 AOF 重写机制,默认值为 64 MB;
  • auto-aof-rewrite-percentage < percent > 设参数表示当 当前AOF文件大小超过上次重写后 AOF 文件大小的百分比时,触发 AOF 重写机制,默认值为 100,将这个值设置为0表示关闭自动AOF重写;
  • 系统自动触发 AOF 重写机制还需要满足以下条件:
    • 当前没有正在执行 BGSAVE 或 BGREWRITEAOF 的子进程;
    • 当前没有正在执行 SAVE 的主进程;
    • 当前没有正在进行集群切换或故障转移;

3)no-appendfsync-on-rewrite:重写时,不会执行appendfsync操作 该参数表示在正在进行AOF重写时不会将AOF缓冲区中的数据同步到旧的AOF文件磁盘,也就是说在进行AOF重写的时候,如果此时有写操作进来,此时写操作的命令会放在aof_buf缓存中(内存中),而不会将其追加到旧的AOF文件中,这么做是为了避免同时写旧的AOF文件和新的AOF文件对磁盘产生的压力。

  • 默认值是 no,表示关闭,即在AOF重写时,会对AOF缓冲区中的数据做同步磁盘操作,这在很大程度上保证了数据的安全性。但是遇到重写操作,可能会发生阻塞。(数据安全,但是性能降低)
  • 值为 yes,表示不写入aof文件,只写入缓存,用户请求不会阻塞,但是在这段时间如果宕机会丢失这段时间的缓存数据。(降低数据安全性,提高性能)

四、Redis4.0 混合持久化

4.1 混合持久化原理

在重启 Redis 时,一般很少使用 RDB 来恢复内存状态,因为会丢失大量数据。通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 RDB 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。

Redis 4.0版本开始 为了解决这个问题,提出了一个混合使用 AOF日志 和 RDB内存快照的方法 —— 混合持久化,简单来说,就是 RDB内存快照以一定的频率执行,在两次 RDB快照之间,使用AOF日志记录这期间的所有命令操作。因此,就不会出现文件过大的情况了,也可以避免重写开销。 于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

混合持久化的加载流程如下: 如下图所示,T1和T2时刻的修改,用AOF日志记录,等到第二次做全量快照时,就可以清空AOF日志,因为此时的修改都已经记录到快照中了,恢复时就不再用日志了。

混合持久化以较小的性能开销保证数据可靠性和性能。

4.2 混合持久化配置

在 Redis 的配置文件当中有一个 aof-use-rdb-preamble 参数来开启 混合持久化,默认是 yes 开启的。混合持久化结合了 RDB 和 AOF 的优点,Redis 5.0 默认是开启的。

1、Redis 命令查询是否开启混合持久化:

1
2
3
127.0.0.1:6379> config get aof-use-rdb-preamble 
1) "aof-use-rdb-preamble"
2) "no"

2、通过命令行开启/关闭混合持久化:

1
2
3
4
5
6
127.0.0.1:6379> config set aof-use-rdb-preamble yes # 开启混合持久化
1) "aof-use-rdb-preamble"
2) "yes"
127.0.0.1:6379> config set aof-use-rdb-preamble no  # 关闭混合持久化
1) "aof-use-rdb-preamble"
2) "yes"

Tips: 命令行设置配置的缺点是重启 Redis 服务之后,设置的配置就会失效。

3、通过 Redis 配置文件开启/关闭混合持久化: 找到 Redis配置文件 redis.conf 文件,把配置文件中的 aof-use-rdb-preamble 设置为 yes / no 分别表示 开启 / 关闭 混合持久化, 如下:

1
2
3
aof-use-rdb-preamble yes # 开启混合持久化
# 或者
aof-use-rdb-preamble no  # 关闭混合持久化

4.3 混合持久化优缺点

优点:

  • 混合持久化结合了 RDB 和 AOF 持久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF的优点,有减低了大量数据丢失的风险。

缺点:

  • AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件的可读性变得很差;
  • 兼容性差,如果开启混合持久化,那么此混合持久化 AOF 文件,就不能用在 Redis 4.0 之前版本了。

五、Redis的主线程,子进程,后台线程

5.1 Redis的主线程,子进程,后台线程

从操作系统的角度来看,进程一般是指资源分配单元,例如一个进程拥有自己的堆、栈、虚存空间(页表)、文件描述符等,而线程一般是指CPU进行调度和执行的实体。

Redis从4.0版本开始,也使用pthread create创建线程,这些线程在创建后,一般会自行执行一些任务,例如执行异步删除任务。相对于完成主要工作的主线程来说,一般可以称这些线程为后台线程。