05_Redis持久化

本笔记来源于:尚硅谷Redis零基础到进阶,最强redis7教程,阳哥亲自带练(附redis面试题)
b站视频

文章来自:
https://github.com/loneasing/mynote

Redis持久化

官网:
https://redis.io/docs/manual/persistence/

Redis持久化(Redis persistence)是指将数据写入持久存储,如固态硬盘(SSD)。

Redis提供了一系列持久性选项。这些包括:

  • RDB(redis 数据库):RDB持久化方式能够在指定的时间间隔对数据进行快照存储。
  • AOF(追加文件):AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾。Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。
  • No persistence(没有持久化):可以完全禁用持久性,这有时在缓存时使用。
  • RDB+AOF:可以同时开启两种持久化方式,在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。

为什么需要持久化

持久化双雄


1. RDB

Redis Database缩写。在指定的时间间隔,对数据进行快照存储。RDB保存到磁盘的文件叫dump.rdb。

实现类似照片记录效果的方式,就是把某一时刻的数据和状态以文件的形式写到磁盘上,也就是快照。这样一来即使故障宕机,快照文件也不会丢失,数据的可靠性也就得到了保证。这个快照文件就称为RDB文件(dump.rdb),其中,RDB就是Redis DataBase的缩写。

1.1 快照

在默认情况下, Redis 将数据库快照保存在名字为 dump.rdb的二进制文件中。可以对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动保存一次数据集。也可以通过调用 SAVE或者 BGSAVE , 手动让 Redis 进行数据集保存操作。

比如说, 以下设置会让 Redis 在满足“ 60 秒内有至少有 1000 个键被改动”这一条件时, 自动保存一次数据集: save 60 1000

配置文件对比

1、Redis6.0.16以下

2、Redis6.2以及Redis-7.0.0

1.2 RDB优缺点

RDB的优点

  • RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题也可以根据需求恢复到不同版本的数据集。
  • RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心,非常适用于灾难恢复。
  • RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能。
  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些。

小总结:

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

RDB缺点

  • 如果希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合,虽然可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),但是Redis要完整的保存整个数据集是一个比较繁重的工作,通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,可能会丢失几分钟的数据。
  • RDB 需要经常fork()子进程来保存数据集到硬盘上,当数据集比较大的时候,fork()的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork(),但是可以调节重写日志文件的频率来提高数据集的耐久度。

小总结:

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

模拟数据丢失:

1.3 RDB配置

  • 配置dump.rdb文件保存路径

    下面是设置到了redis安装目录,当然也可以设置到其他目录下。默认是./

    2023-04-11_225736

  • 修改快照文件名

    快照文件名默认为dump.rdb,可以对其进行修改

    2023-04-12_025908

1.4 触发RDB快照和恢复

1.4.1 自动触发
  • 设置redis.config文件中的自动触发时间:save <seocnds> <changes>

    • 修改为7秒内3次修改:save 7 3

2023-04-11_222730

  • 修改dump文件保存路径

  • 修改dump文件名称

  • 添加数据触发快照

    2023-04-11_223005

  • FLUSHDB和FLUSHALL命令

    执行flushdb或者flushall命令也会触发RDB快照,不过里面是空的,以便下次启动redis服务是读取到的就是空文件。

  • SHUTDOWN命令

    执行shutdown命令之前会将当前的数据进行一次快照保存。

注:RDB 持久化是 Redis 的一种持久化机制,它会在 Redis 数据发生修改时对内存中的数据进行快照,然后保存到磁盘,以保证数据的持久性。通常情况下,RDB 保存快照的时间间隔由配置文件中的参数 save 决定,格式为 save <seconds> <changes>,表示在 <seconds> 秒内,如果数据有 <changes> 次修改,则会进行一次快照。
在题目描述的情况下,RDB 设置了每 7 秒进行一次快照,但是如果在 7 秒内修改次数超过了 3 次,也会进行快照。这是因为在 Redis 中,保存快照并不是在规定的时间到达后才进行,而是在修改数据时和时间间隔条件的双重限制下才进行的。

如果限制只按时间间隔来进行保存快照,则会出现两个问题
如果时间间隔太大,那么 Redis 持久化的数据可能会丢失,并且故障恢复时的数据可能会受到影响。
如果时间间隔太小,那么数据的保存成本就会过高,并可能导致 Redis 运行效率下降。

因此,Redis 引入了按时间和数据修改次数双重限制的快照保存机制,以在灵活性和效率之间取得平衡。如果在 7 秒内修改的次数超过 3 次,则说明数据的变化较快,在此情况下保存快照并不会带来明显的性能问题。因此,Redis 将其纳入保存快照的范围,以保证数据的安全和一致性

1.4.2 手动触发
  • SAVE命令 (生产环境下不要使用,会导致服务阻塞)

    SAVE命令在主程序中执行会阻塞当前进程,直到持久化工作完成,redis才能处理其他命令。工作中禁止使用该命令。

    1
    2
    3
    4
    5
    6
    127.0.0.1:6379> set name Alice
    OK
    127.0.0.1:6379> set age 18
    OK
    127.0.0.1:6379> save # 立马将数据保存到快照中,如果数据量大会造成阻塞
    OK

    2023-04-12_010123

  • BGSAVE命令 (默认,生产环境推荐)

    BGSAVE命令会fork一个子进程在后台异步进行持久化工作,持久化期间redis可以执行其他命令。

    1
    2
    3
    4
    5
    6
    127.0.0.1:6379> set name Cindy
    OK
    127.0.0.1:6379> set age 20
    OK
    127.0.0.1:6379> BGSAVE
    Background saving started # 在后台执行持久化

    2023-04-12_010220

在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后会exec系统调用,处于效率考虑,尽量避免膨胀。

LASTSAVE命令可以获取最后一次成功执行快照的时间。得到的是一个时间戳,可以通过date -d @时间戳命令获取对应的时间。

1
2
3
4
5
127.0.0.1:6379> LASTSAVE
(integer) 1681223083
127.0.0.1:6379> quit
[root@redis ~]# date -d @1681223083
2023年 04月 11日 星期二 22:24:43 CST
1.4.3 修复快照数据

有些情况下快照保存的数据不完整导致无法读取快照数据,可以使用redis-check-rdb命令对rdb文件进行修复。

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@redis redis-7.0.10]# redis-check-rdb dump.rdb 
[offset 0] Checking RDB file dump.rdb
[offset 27] AUX FIELD redis-ver = '7.0.10'
[offset 41] AUX FIELD redis-bits = '64'
[offset 53] AUX FIELD ctime = '1681233313'
[offset 68] AUX FIELD used-mem = '1104992'
[offset 80] AUX FIELD aof-base = '0'
[offset 82] Selecting DB ID 0
[offset 155] Checksum OK
[offset 155] \o/ RDB looks OK! \o/
[info] 8 keys read
[info] 0 expires
[info] 0 already expired
1.4.4 恢复快照数据

redis在启动服务时会读取配置的快照保存路径中的dump.rdb文件,所以只需要将备份的rdb文件放到配置的保存路径中,然后启动redis服务即可还原快照中的数据。

1.4.5 哪些情况会触发RDB快照
  1. 配置文件中默认的快照配置
  2. 手动save/bgsave命令
  3. 执行flushdb/fulshall命令也会产生dump.rdb文件,但是也会将命令记录到dump.rdb文件中,恢复后依旧是空,无意义
  4. 执行shutdown且没有设置开启AOF持久化
  5. 主从复制时,主节点自动触发

1.5 禁用RDB快照

方法一

redis-cli config set save "":将save的值设置为空,即禁用了快照功能。

方法二

手动修改配置文件

1.6 RDB优化配置项详解

配置文件SNAPSHOTTING模块

  • save <seconds> <changes>:配置快照保存条件
  • dir:配置快照保存目录地址
  • dbfilename:配置快照的文件名
  • stop-writes-on-bgsave-error:

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

  • rdbcompression:

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

  • rdbchecksum:

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

  • rdb-del-sync-files:

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

小总结:


2. AOF

Append Only File缩写。记录每次对服务器的写操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。

2.1 AOF持久化工作流程

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

2.2 AOF缓冲区三种写回策略

ALways:同步写回,每个写命令执行完立刻同步地将日志写会磁盘

everysec:(默认)每秒写回,每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔1秒把缓冲区中的内容写入到磁盘

no:操作系统控制的写回,每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘

小总结:

2.3 AOF优缺点

AOF优点:

  • 使用AOF 会让Redis更加耐久:可以使用不同的写回(fsync)策略:no、everysec、always。使用默认的everysec策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,最多丢失1秒的数据。
  • AOF文件是一个只进行追加的日志文件,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,也也可使用redis-check-aof工具修复这些问题。
  • Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 如果不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

AOF缺点:

  • 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
  • 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入时,RDB 可以提供更有保证的最大延迟时间。

2.4 AOF配置

开启AOF

AOF默认是关闭的,需要将配置文件中appendonly设置为yes。

2023-04-12_025125

配置aof文件保存路径
  • redis6及以前

AOF保存文件的位置和RDB保存文件的位置一样,都是通过redis.conf配置文件的dir配置

  • redis7最新

一句话:

MP-AOF实现

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

  • BASE: 表示基础AOF,它一般由子进程通过重写产生,该文件最多只有一个。
  • INCR:表示增量AOF,它一般会在AOFRW开始执行时被创建,该文件可能存在多个。
  • HISTORY:表示历史AOF,它由BASE和INCR AOF变化而来,每次AOFRW成功完成时,本次AOFRW之前对应的BASE和INCR AOF都将变为HISTORY,HISTORY类型的AOF会被Redis自动删除。

为了管理这些AOF文件,我们引入了一个manifest (清单)文件来跟踪、管理这些AOF。同时,为了便于AOF备份和拷贝,我们将所有的AOF文件和manifest文件放入一个单独的文件目录中,目录名由appenddirname配置(Redis 7.0新增配置项)决定。

Redis7.0config 中对应的配置项

修改aof文件名和目录名
  • redis6及以前 ,有且仅有一个

  • Redis7 Multi Part AOF的设计

从1个文件到3个文件

生成的aof目录名默认为appendonlydir,文件名默认为appendonly.aof,可以在配置文件中修改。

image-20230412032737462

2.5 AOF的恢复和修复

  • AOF文件数据恢复

    在同时开启RDB和AOF持久化时,重启redis服务只会加载aof文件,不会加载rdb文件,即使启动时没有appendonlydir目录,也会创建一个新的appendonlydir目录。

    2023-04-12_050612

  • AOF文件数据修复

    在开启了AOF后,当AOF文件出现异常时,redis服务无法正常启动。可以使用redis-check-aof --fix 文件名命令修复文件。

    2023-04-12_053017

  • AOF紧急恢复

    如果误执行了FLUSHALL操作,先停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。因为FLUSHALL或者FLUSHDB也是写命令,会被追加到aof文件中。

    2023-04-12_060804

2.6 触发AOF日志重写

因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。

举个例子, 如果对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要记录100 条记录。然而在实际上, 只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。

为了处理这种情况, Redis 可以在不打断服务客户端的情况下, 对 AOF 文件进行重建,即自动执行BGREWRITEAOF 命令, Redis 将生成一个新的 AOF 文件, 这个文件包含重建当前数据集所需的最少命令。Redis 2.2 需要自己手动执行 BGREWRITEAOF 命令; Redis 2.4后则可以自动触发 AOF 重写。

2.6.1 自动触发

自动触发需要满足配置文件中的设置,官方默认设置是:
auto-aof-rewrite-percentage 100:根据上次重写后的aof大小,判断当前aof大小是不是增长了1倍。100%表示一倍。
auto-aof-rewrite-min-size 64mb:重写时满足的文件大小,即incr.aof文件超过了64兆才会重写。

注意同时满足这两个条件才会触发。

下面将auto-aof-rewrite-min-size改为1kb,测试AOF日志重写

2023-04-12_071956

rdb文件的内容是无法查看的,所以还是无法确定重写后的数据是否保存到了base.rdb文件中,可以先将aof-use-rdb-preamble设置为no,表示禁用aof+rdb混合模式,删除appendonlydir目录然后重启redis服务,这样就不会生成base.rdb文件,而是生成base.aof文件,重复上面的写操作,最后查看aof文件内容。

2023-04-12_073207

可以看到,重写的数据确实写入了base文件,并且base.rdb文件大小比base.aof文件大小小得多,所以通常建议开启aof+rdb混合模式。

2.6.2 手动触发

执行命令BGREWRITEAOF即可执行AOF重写。

2023-04-12_074803

2.7 AOF重写原理

目的:压缩AOF文件

由于AOF持久化是Redis不断将写命令记录到 AOF 文件中,随着Redis不断的进行,AOF 的文件会越来越大,文件越大,占用服务器内存越大以及 AOF 恢复要求时间越长。

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

触发机制
  • 官网默认配置

  • 自动触发
    满足配置文件中的选项后,Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时

  • 手动触发
    客户端向服务器发送bgrewriteaof命令

案例说明

需求说明:

$\textcolor{red}{启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。}$ $\textcolor{blue}{举个例子:}$ 比如有个key 开始你 set k1 v1 然后改成 set k1 v2 最后改成 set k1 v3 如果不重写,那么这3条语句都在aof文件中,内容占空间不说启动的时候都要执行一遍,共计3条命令但是,我们实际效果只需要set k1 v3这一条,所以, 开启重写后,只需要保存set k1 3就可以了只需要保留最后一次修改值,相当于给aof文件瘦身减肥,性能更好。 AOF重写不仅降低了文件的占用空间,同时更小的AOF也可以更快地被Redis加载。

需求验证:

$\textcolor{green}{启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。}$

步骤:

  • 前期配置准备:

    1. 开启aof,appendonly yes,设置aof持久化开启
    2. 重写峰值修改为1k
    3. 关闭混合,设置为no
    4. 删除执勤啊的全部aof和rdb,清除干扰项
  • 自动触发案例01

    1. 完成上述正确配置,重启redis服务器,执行 set k1 v1 查看aof文件是否正常
    2. 查看aof三大 配置文件 appendonly.aof.1.base.aof;appendonly.aof.1.incr.aof;appendonly.aof.manifest
    3. k1不停的更新值
    4. 重写触发

  • 手动触发案例02
    客户端向服务器发送bgrewriteaof命令

  • 结论

也就是说AOF文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的AOF文件。
AOF文件重写触发机制:通过 redis.conf配置文件中的 auto-aof-rewrite-percentage:默认值为100,以及auto-aof-rewrite-min-size: 64mb配置,也就是说默认Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。

重写原理
  1. 在重写开始前,redis会fork一个“重写子进程”,这个子进程会读取现有的AOF文件,并将其包含的指令进行分析压缩并写入到一个临时文件中。
  2. 与此同时,主进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件中,这样做是保证原有的AOF文件的可用性,避免在重写过程中出现意外。
  3. 当“重写子进程”完成重写工作后,它会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新AOF文件中。
  4. 当追加结束后,redis就会用新AOF文件来代替旧AOF文件,之后再有新的写指令,就都会追加到新的AOF文件中。
  5. 重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。

2.8 AOF 优化配置项详解

配置文件 APPEND ONLY MODE模块

2.9 小总结

3. AOF+RDB混合模式

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

同时开启两种持久化方式

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

推荐方式:RDB+AOF混合方式

首先开启AOF:appendonly yes,默认是关闭的。

开启AOF+RDB混合模式:aof-use-rdb-preamble yes,默认是开启的。

同时开启AOF和RDB持久化,当Redis重启时会优先加载AOF文件来恢复原始的数据,因为在通常情况下,AOF保存的数据集要比RDB文件保存的数据集要完整。

在持久化时,先使用RDB进行快照存储,然后使用AOF持久化记录所有写的操作,当重写策略满足或者手动触发重写的时候,将最新的数据存储为新的RDB记录。这样的话,重启服务的时候会从RDB和AOF两部分恢复数据,既保证了数据的完整性,又提高了恢复数据的性能。

4. 纯缓存模式

纯缓存模式即同时关闭AOF和RDB,这样可以最大化redis的读写性能,但无法保证数据的安全性。

关闭RDB:修改配置文件save ""

禁用RDB持久化模式,仍然可以使用命令save、bgsave生成rdb文件。

关闭AOF:修改配置文件appendonly no

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


05_Redis持久化
http://yuanql.top/2023/05/20/11_Redis/Redis_尚硅谷/Redis基础篇/05_Redis持久化/
作者
Qingli Yuan
发布于
2023年5月20日
许可协议