Redis7.0/6.0双版本的RDB/AOF持久化

2023年 7月 25日 83.5k 0

前言

本文将讲述RDB持久化,跟AOF持久化,混合持久化,AOF在7.0版本变化很大,AOF持久化将会讲述7.0版本和之前的6.0版本,并且讲述为什么要改变AOF持久化,6.0的AOF有什么问题吗?

为什么需要持久化?

为什么需要就不用多说了吧,没有持久化,基于内存的数据,一旦数据库宕机,数据就全没了。

RDB(Redis DataBase)

介绍:

RDB(Redis 数据库):RDB 持久性以指定的时间间隔执行数据集的时间点快照。

能干嘛:

在指定的时间间隔内将内存中的数据集快照写入磁盘。

Redis的数据都在内存中,保存RDB备份时它执行的是全量快照,也就是说,把内存中的所有数据都记录到磁盘中,一锅端。

rdb保存的是dump.rdb文件,默认在安装目录下。

自动触发设置

redis7版本,按照redis.conf里配置save seconds cahnges;即可指定几秒中修改了几次就备份一次快照

修改dump的文件保存路径: 可以连接到redis,使用config get dir命令获取默认路径,也可使用set修改

修改dump文件名称: 可在redis.conf配置文件中找到dbfilename dump.rdb;修改该配置项即可,

触发备份有两种情况: 第一种就是达到几秒了修改了几次,第二种的话达到修改几次也可以进行备份快照

如何修复: 将备份文件移动到redis安装目录下并启动服务即可;一般备份文件都会跟redis服务分机隔离,避免redis服务机down了,备份文件也没了

手动触发设置

Redis提供了两个命令来生成RDB文件,分别是save和bgsave。

save: 在主程序执行会阻塞当前redis服务器,直到持久化工作完成;执行save命令期间,Redis不会处理其他命令。会堵塞redis服务,堵塞客户端请求不用这个。

bgsave(默认):

Redis会在后台异步进行快照操作,不堵塞;备份快照的同时还可以响应客户端请求,该触发方式会fork一个跟主进程一样的子进程进行持久化操作。

Redis会使用bgsave对当前内存中所有数据做快照,这个操作是子进程在后台完成的,这就允许主进程同时可以修改数据

lastsave: 可以使用该命令获取最后一次成功执行快照的时间

这里就会有一个问题:

执行 bgsave 过程中,由于是交给子进程来构建 RDB 文件,主线程还是可以继续工作的,共享内存是只读的,为什么能修改呢?

关键的技术就在于写时复制技术(Copy-On-Write, COW)。

在fork出子进程后,父子进程是共享一块物理内存 (并且是只读内存) 的,父进程主线程在执行客户端读命令时,并没有有任何的影响,而在执行写命令时,客户端会将需要修改的内存数据,复制出一份 (这块数据的物理内存就会被复制一份) 数据副本 (父进程将在数据副本中做出修改) ,不会影响子进程执行RDB快照。

该数据副本是在父进程的内存中,并不影响父子进程的共享数据。

也就是当父进程需要修改共享内存中的数据时,就会发生写时复制技术,父进程将在数据副本中执行写操作,不影响子进程备份fork主进程时(原本的)的数据。

可以看得出来在执行RDB快照时,父进程主线程执行的写命令后的数据是无法被备份到RDB快照的,只能交由下一次的 bgsave 快照,这也能理解,毕竟我只是想备份当时原本的数据。

RDB持久化.png

另外,写时复制的时候会出现这么个极端的情况:

我们知道当触发写时复制时会复制出一份数据副本,那么极端情况下,如果所有的共享内存都被修改,则此时的内存占用是原先的 2 倍。

执行完成快照后的新的RDB快照就会替换旧的RDB快照,至于父进程创建出的数据副本我估计应该是合并了,或者就保留在内存中了。

优势

  • 适合大规模的数据恢复(快照时一个时间点的全量数据)
  • 可以按照业务定时备份
  • RDB文件在执行数据恢复时,比同数据量的AOF日志文件快很多,因为RDB恢复只需要将该文件加载到内存即可,而AOF需要执行命令

劣势

  • 数据完整性不高: 需要设置一定频率进行一次RDB快照,如果设置的频率高了,如果redis意外down掉的话,就会丢失最后一次成功快照到当前的所有数据。
  • 性能问题: 如果设置频率低了,则会影响Redis的性能,因为执行fork时,该操作也是在主线程中执行的,如果数据量很大则fork操作的耗时则会很长。
  • 内存问题: 写时复制时将会复制一份物理内存,极端情况内存占用量是原来的2倍。

如何检查恢复dump.rdb文件

在备份的过程中可能出现一条数据在备份一半的时候,机器突然down了,这个时候备份文件就是残缺的,无法正确执行的。

可以使用/usr/local/bin目录下的redis-check-rdb命令恢复【redis-check-rdb dump.rdb】

哪些情况会触发RDB快照

  • 配置文件中默认的快照配置
  • 手动save/bgsave命令
  • 执行flushall/flushdb命令也会产生dump.rdb文件,但是是空的,无意义
  • 执行shutdown且没有设置开启AOF持久化
  • 主从复制时,主节点自动触发

如何禁用快照

  • 动态停止RDB保存规则的方法:redis-cli config set save ""
  • 在redis.conf文件中配置 save ""
  • 两个方法本质一样,建议用第二种

RDB配置项详解

redis.conf配置文件中的snapshotting模块

  • save seconds changes --配置快照规则,别看是save,其实是bgsave。

  • dbfilename --快照文件名称

  • dir --配置快照文件路径

  • stop-writes-on-bgsave-error

    • 默认yes;如果配置成no,表示你不在乎数据不一致或者有其他的手段发现和控制这种不一致,那么在快照写入失败时,也能确保redis继续接受新的写请求
  • rdbcompression

    • 默认yes;对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。 如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能
  • rdbchecksum

    • 默认yes;在存储快照后,还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能
  • rdb-del-sync-files

    • 在没有持久性的情况下删除复制中使用的RDB文件启用。默认情况下no,此选项是禁用的。

AOF(Append Only File)

介绍:

以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来,只许追加文件不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

默认情况下redis是没有开启AOF的。开启AOF功能需要在redis.conf配置appendonly yes

AOF保存的日志文件是appendonly.aof文件,在redis6版本只有一个该文件,而到了7版本有了三个不同的文件

AOF持久化工作流程

1 Client(客户端)作为命令的来源,会有多个源头以及源源不断的请求命令。
2 在这些命令到达Redis Server 以后并不是直接写入AOF文件,会将其这些命令先放入AOF缓存区中进行保存。这里的AOF缓冲区实际上是内存中的一片区域,存在的目的是当这些命令达到一定量以后再写入磁盘,避免频繁的磁盘IO操作。
3 AOF缓冲会根据AOF缓冲区同步文件的三种写回策略将命令写入磁盘上的AOF文件。
4 随着写入AOF内容的增加为避免文件膨胀,会根据规则进行命令的合并(又称AOF重写) ,从而起到AOF文件压缩的目的。
5 当Redis Server 服务器重启的时候会从AOF文件载入数据。

AOF缓冲区的三种写回策略

  • always --同步写回,每个写命令执行完立刻同步地将日志写回磁盘【注意这里的同步不是异步同步的同步,而是指立刻将写命令写回磁盘】

    • 优点:可靠性高,数据不丢失
    • 缺点:每个写命令都要一次磁盘IO,性能影响很大
  • everysec --每秒写回,每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区内,每隔1秒把缓冲区的内容写入磁盘

    • 优点:性能适中
    • 缺点:down机时只会丢失1秒内的数据
  • no --操作系统控制的写回,每个写命令执行完,只是先把日志写到AOF的内存缓冲区,由操作系统决定何时将缓冲区内容写入磁盘

    • 优点:性能好
    • 缺点:down机时容易丢失一堆数据

AOF操作

如何开启: 在redis.conf中配置appendonly yes

设置写回策略: 在redis.conf中配置appendfsync everysec;使用每秒写回策略

AOF文件保存路径:

  • redis6的时候保存文件位置跟RDB保存位置一样,都是通过配置dir
  • redis7之后多了个appenddirname配置项,可以配置AOF文件路径
  • 可做紧急恢复:即使不小心删除了redis的全部数据,只要没有执行日志的重写(因为执行删了后进行日志重写,重写是读取当前数据库的数据,此时数据库没有数据,所以重写后的基本AOF也没有数据了,并且旧的基本AOF和增量AOF会被清除),就可以在增量AOF文件中找到删除的这个写操作,把该操作去除,重启服务器即可恢复全部数据。

AOF文件名称:

  • 在redis6,只有一个AOF文件

  • redis7后有三个:

    • base基本文件:该文件最大只有一个
    • incr增量文件:该文件有多个
    • historyAOF历史类型文件,它由base跟incr AOF变化而来,每次AOFRW成功完成时,本次AOFRW之前对应的base跟incr AOF都将变为history,history类型的AOF会被Redis自动删除
    • 并且引入了一个清单文件(manifest),来标识管理每个文件的类型

AOF文件的异常恢复

假设突然宕机写回aof的操作执行到一半断了,此时AOF文件是残缺的无法执行

可以使用redis-check-aof --fix 备份文件 该命令进行恢复AOF文件

优势

  • 更好的保护收据不丢失:每秒写入,只会丢失一秒内的数据
  • 性能高:每秒写入磁盘,对比RDB的全量写入,性能高很多
  • 可做紧急恢复:即使不小心删除了redis的全部数据,也可以在AOF文件中找到删除的这个写操作,把该操作去除,重启服务器即可恢复全部数据。

劣势

  • 相同的数据集的数据而言AOF文件要远大于rdb文件。
  • 数据恢复也慢于RDB恢复,因为需要一条条命令执行。

AOF重写

为什么需要?

因为AOF文件中存储的是指令数据,一条命令就转变成多条指令存储下来,那么就会存在很多无效数据,都会被后面的指令给覆盖,这些被覆盖的指令没有意义浪费空间,所以我们需要重写AOF文件,只保留可以恢复数据的最小指令集,这样这些指令就不会被记录了。

触发时机

自动触发

根据配置文件

auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64m

  • 根据上次重写后的aof大小,判断当前aof大小是不是增长了1倍,如果是200就是两倍
  • 重写时满足的文件大小,重写时文件大小是否超过了64m

注意 ,同时满足,且的关系才会自动触发。

手动触发

客户端向服务器发送bgrewriteaof命令

7.0版本之前的过程

  • Redis会fork一个子进程,所以现在我们有了一个子进程和一个父进程,父子进程共享物理内存数据(只读)。

  • 如果主进程执行一条写命令,则会使用写时复制技术,父进程内存空间创建一个数据副本,在副本中执行写操作,这样不影响子进程重写AOF。

  • 重写过程主进程也能处理写命令,为了能让子进程也获得到这些变化并保存重写后的AOF数据跟当前数据库数据是一致的,父进程会新开辟一块名为AOF重写缓冲区的内存空间。

  • 父进程需要将在重写过程中新产生的写命令保存到AOF缓冲区和AOF重写缓冲区中。

  • AOF缓冲区的时候还是会每秒写入到当前AOF文件中,这样做的好处就是即使重写失败也没有数据安全问题。
  • 重写子进程需要将从内存读取的数据以指令形式写入临时AOF文件中。

  • 当子进程重写完时,父进程获得一个信号:

  • 父进程就会将AOF重写缓冲区中的数据写入到临时AOF文件中,此时临时AOF文件数据就跟数据库数据保存一致了。
  • 修改临时AOF文件名称,覆盖旧的AOF文件。
    DESC
    该图来源于小林coding,上面的子进程的数据副本其实是不存在的,忽略掉就行了。
  • 7.0版本之前单个AOF文件的劣势在于重写的过程中

    • 内存开销:新开辟AOF重写缓冲区,这样写命令都会保存两份冗余数据在内存中。
    • CPU开销:写入AOF重写缓冲区的数据会消耗CPU资源。
    • 磁盘IO开销:重写缓冲区数据在子进程完成重写AOF后将会写入临时AOP文件。

    7.0版本过程

  • Redis会fork一个子进程,所以现在我们有了一个子进程和一个父进程,父子进程共享物理内存数据(只读)。

  • 如果主进程执行一条写命令,则会使用写时复制技术,父进程内存空间创建一个数据副本,在副本中执行写操作,这样不影响子进程重写AOF。

  • 子进程开始在临时AOF文件中写入新的baseAOF。

  • 读取内存中的数据,以命令的形式记录进baseAOF。
  • 父进程打开一个新的增量AOF文件继续写更新。如果重写失败,旧的基本文件和增量文件(如果有的话)加上这个新打开的增量文件代表完整的更新数据集,所以我们是安全的。

  • 当子进程完成重写baseAOF时,父进程获得一个信号:

  • 父进程会负责更新manifest文件,将新生成的BASE AOF和INCR AOF信息加入进去,之后将执行原子替换操作替换掉旧的增量AOF文件和基本AOF文件,并将之前的文件设置成history类型。
  • 这样AOF重写的结果就生效了。后台线程会自动清理history类型的文件。
  • 生效后的AOF基本文件,和AOF增量文件就是我们完整的数据备份。

  • 注意:

    • 7.0的触发重写是看增量AOF文件是否达到了要求,数据也是写入到增量AOF文件,而不是基本AOF文件,基本文件只有重写时才会有,并且基本文件都是最小的指令集。
    • 重写AOF基本文件的操作,并没有读取旧的AOF基本和增量文件,而是将整个内存中的数据库内容用指令的方式重写了一个新的AOF基本文件,这点和快照有点类似。

    在7.0的版本重写过程中对比之前的优势

  • 我们除去了AOF重写缓冲区,降低了内存开销,
  • 我们除去了AOF重写缓冲区,也就不需要将数据冗余的保存到内存,降低了CPU开销,
  • 我们除去了AOF重写缓冲区,也就不需要将缓冲区数据写入临时AOF文件,降低了磁盘IO开销。
  • RDB和AOF混合持久化

    混合持久化默认就是开启的

    aof-use-rdb-preamble yes

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

    在这种情况下,当redis重启的时候会优先加载aof文件来恢复原始数据,因为通常情况下aof文件保存的数据集要比rdb保存的数据集要完整

    rdb的数据不实时,同时使用两者时候服务器重启也只会在aof文件。也别只是用aof,因为rdb更适合用于备份数据库(aof不断变化不好备份),留着rdb作为一个万一的手段。

    所以我们建议开启混合持久化

    混合持久化本质是通过 AOF 后台重写(bgrewriteaof 命令)完成的

    结合了RDB和AOF的优点,既能快速加载又能避免丢失过多的数据。

    1 开启混合方式设置

    设置aof-use-rdb-preamble的值为 yes yes表示开启,设置为no表示禁用

    2 RDB+AOF的混合方式---------> 结论:RDB做全量持久化,AOF做增量持久化

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

    原本的baseaof文件格式是aof现在变成了rdb

    6.0版本Redis混合持久化

    6.0的混合持久化本质也是通过 AOF 后台重写(bgrewriteaof 命令)完成的。

    当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与父进程共享的内存数据以 RDB 方式写入到 AOF 文件,然后父进程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件

    使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论