怎么掌握Redis持久化RDB和AOF

37次阅读
没有评论

共计 8484 个字符,预计需要花费 22 分钟才能阅读完成。

本篇内容介绍了“怎么掌握 Redis 持久化 RDB 和 AOF”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让丸趣 TV 小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、为什么需要持久化?

Redis 对数据的操作都是基于内存的,当遇到了进程退出、服务器宕机等意外情况,如果没有持久化机制,那么 Redis 中的数据将会丢失无法恢复。有了持久化机制,Redis 在下次重启时可以利用之前持久化的文件进行数据恢复。Redis 支持的两种持久化机制:

RDB:把当前数据生成快照保存在硬盘上。
AOF:记录每次对数据的操作到硬盘上。

二、RDB 持久化

在指定的时间间隔内将内存中的数据集快照写入磁盘,它恢复时是将快照文件直接读到内存里。RDB(Redis DataBase)持久化是把当前 Redis 中全部数据生成快照保存在硬盘上。RDB 持久化可以手动触发,也可以自动触发。

1、备份是如何执行的?

redis 会单独创建(fork)一个子进程来进行持久化,会先将数据写入到临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好了的文件。整个过程中,主进程是不进行任何 IO 操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对数据的恢复完整性不是非常敏感, 那么 RDB 方式要比 AOF 方式更加的高效。RDB 的缺点是最后一次持久化的数据可能丢失。

2、RDB 持久化流程

3、手动触发

save 和 bgsave 命令都可以手动触发 RDB 持久化。

save
执行 save 命令会手动触发 RDB 持久化,但是 save 命令会阻塞 Redis 服务,直到 RDB 持久化完成。当 Redis 服务储存大量数据时,会造成较长时间的阻塞,不建议使用。

bgsave
执行 bgsave 命令也会手动触发 RDB 持久化,和 save 命令不同是:Redis 服务一般不会阻塞。Redis 进程会执行 fork 操作创建子进程,RDB 持久化由子进程负责,不会阻塞 Redis 服务进程。Redis 服务的阻塞只发生在 fork 阶段,一般情况时间很短。
bgsave 命令的具体流程如下图:

1、执行 bgsave 命令,Redis 进程先判断当前是否存在正在执行的 RDB 或 AOF 子线程,如果存在就是直接结束。
2、Redis 进程执行 fork 操作创建子线程,在 fork 操作的过程中 Redis 进程会被阻塞。
3、Redis 进程 fork 完成后,bgsave 命令就结束了,自此 Redis 进程不会被阻塞,可以响应其他命令。
4、子进程根据 Redis 进程的内存生成快照文件,并替换原有的 RDB 文件。
5、同时发送信号给主进程,通知主进程 rdb 持久化完成,主进程更新相关的统计信息(info Persitence 下的 rdb_* 相关选项)。

4、自动触发

除了执行以上命令手动触发以外,Redis 内部可以自动触发 RDB 持久化。自动触发的 RDB 持久化都是采用 bgsave 的方式,减少 Redis 进程的阻塞。那么,在什么场景下会自动触发呢?

在配置文件中设置了 save 的相关配置,如 sava m n,它表示在 m 秒内数据被修改过 n 次时,自动触发 bgsave 操作。

当从节点做全量复制时,主节点会自动执行 bgsave 操作,并且把生成的 RDB 文件发送给从节点。

执行 debug reload 命令时,也会自动触发 bgsave 操作。

执行 shutdown 命令时,如果没有开启 AOF 持久化也会自动触发 bgsave 操作。

5、RDB 优点

RDB 文件是一个紧凑的二进制压缩文件,是 Redis 在某个时间点的全部数据快照。所以使用 RDB 恢复数据的速度远远比 AOF 的快,非常适合备份、全量复制、灾难恢复等场景。

6、RDB 缺点

每次进行 bgsave 操作都要执行 fork 操作创建子经常,属于重量级操作,频繁执行成本过高,所以无法做到实时持久化,或者秒级持久化。

另外,由于 Redis 版本的不断迭代,存在不同格式的 RDB 版本,有可能出现低版本的 RDB 格式无法兼容高版本 RDB 文件的问题。

7、dump.rdb 中配置 RDB

快照周期:内存快照虽然可以通过技术人员手动执行 SAVE 或 BGSAVE 命令来进行,但生产环境下多数情况都会设置其周期性执行条件。

Redis 中默认的周期新设置

#  周期性执行条件的设置格式为
save  seconds   changes 
#  默认的设置为:save 900 1
save 300 10
save 60 10000
#  以下设置方式为关闭 RDB 快照功能
save 

以上三项默认信息设置代表的意义是:

如果 900 秒内有 1 条 Key 信息发生变化,则进行快照;

如果 300 秒内有 10 条 Key 信息发生变化,则进行快照;

如果 60 秒内有 10000 条 Key 信息发生变化,则进行快照。读者可以按照这个规则,根据自己的实际请求压力进行设置调整。

其它相关配置

#  文件名称
dbfilename dump.rdb
#  文件保存路径
dir ./
#  如果持久化出错,主进程是否停止写入
stop-writes-on-bgsave-error yes
#  是否压缩
rdbcompression yes
#  导入时是否检查
rdbchecksum yes

dbfilename:RDB 文件在磁盘上的名称。

dir:RDB 文件的存储路径。默认设置为“./”,也就是 Redis 服务的主目录。

stop-writes-on-bgsave-error:上文提到的在快照进行过程中,主进程照样可以接受客户端的任何写操作的特性,是指在快照操作正常的情况下。如果快照操作出现异常(例如操作系统用户权限不够、磁盘空间写满等等)时,Redis 就会禁止写操作。这个特性的主要目的是使运维人员在第一时间就发现 Redis 的运行错误,并进行解决。一些特定的场景下,您可能需要对这个特性进行配置,这时就可以调整这个参数项。该参数项默认情况下值为 yes,如果要关闭这个特性,指定即使出现快照错误 Redis 一样允许写操作,则可以将该值更改为 no。

rdbcompression:该属性将在字符串类型的数据被快照到磁盘文件时,启用 LZF 压缩算法。Redis 官方的建议是请保持该选项设置为 yes,因为“it’s almost always a win”。

rdbchecksum:从 RDB 快照功能的 version 5 版本开始,一个 64 位的 CRC 冗余校验编码会被放置在 RDB 文件的末尾,以便对整个 RDB 文件的完整性进行验证。这个功能大概会多损失 10% 左右的性能,但获得了更高的数据可靠性。所以如果您的 Redis 服务需要追求极致的性能,就可以将这个选项设置为 no。

8、RDB 更深入理解

由于生产环境中我们为 Redis 开辟的内存区域都比较大(例如 6GB),那么将内存中的数据同步到硬盘的过程可能就会持续比较长的时间,而实际情况是这段时间 Redis 服务一般都会收到数据写操作请求。那么如何保证数据一致性呢?
RDB 中的核心思路是 Copy-on-Write,来保证在进行快照操作的这段时间,需要压缩写入磁盘上的数据在内存中不会发生变化。在正常的快照操作中,一方面 Redis 主进程会 fork 一个新的快照进程专门来做这个事情,这样保证了 Redis 服务不会停止对客户端包括写请求在内的任何响应。另一方面这段时间发生的数据变化会以副本的方式存放在另一个新的内存区域,待快照操作结束后才会同步到原来的内存区域。
举个例子:如果主线程对这些数据也都是读操作(例如图中的键值对 A),那么,主线程和 bgsave 子进程相互不影响。但是,如果主线程要修改一块数据(例如图中的键值对 C),那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。

在进行快照操作的这段时间,如果发生服务崩溃怎么办?
很简单,在没有将数据全部写入到磁盘前,这次快照操作都不算成功。如果出现了服务崩溃的情况,将以上一次完整的 RDB 快照文件作为恢复内存数据的参考。也就是说,在快照操作过程中不能影响上一次的备份数据。Redis 服务会在磁盘上创建一个临时文件进行数据操作,待操作成功后才会用这个临时文件替换掉上一次的备份。

可以每秒做一次快照吗?
对于快照来说,所谓“连拍”就是指连续地做快照。这样一来,快照的间隔时间变得很短,即使某一时刻发生宕机了,因为上一时刻快照刚执行,丢失的数据也不会太多。但是,这其中的快照间隔时间就很关键了。
如下图所示,我们先在 T0 时刻做了一次快照,然后又在 T0+t 时刻做了一次快照,在这期间,数据块 5 和 9 被修改了。如果在 t 这段时间内,机器宕机了,那么,只能按照 T0 时刻的快照进行恢复。此时,数据块 5 和 9 的修改值因为没有快照记录,就无法恢复了。

针对 RDB 不适合实时持久化的问题,Redis 提供了 AOF 持久化方式来解决

三、AOF 持久化

AOF(Append Only File)持久化是把每次写命令追加写入日志中,当需要恢复数据时重新执行 AOF 文件中的命令就可以了。AOF 解决了数据持久化的实时性,也是目前主流的 Redis 持久化方式。

Redis 是“写后”日志,Redis 先执行命令,把数据写入内存,然后才记录日志。日志里记录的是 Redis 收到的每一条命令,这些命令是以文本形式保存。PS: 大多数的数据库采用的是写前日志(WAL),例如 MySQL,通过写前日志和两阶段提交,实现数据和逻辑的一致性。

而 AOF 日志采用写后日志,即先写内存,后写日志。

为什么采用写后日志?
Redis 要求高性能,采用写日志有两方面好处:

避免额外的检查开销:Redis 在向 AOF 里面记录日志的时候,并不会先去对这些命令进行语法检查。所以,如果先记日志再执行命令的话,日志中就有可能记录了错误的命令,Redis 在使用日志恢复数据时,就可能会出错。

不会阻塞当前的写操作

但这种方式存在潜在风险:

如果命令执行完成,写日志之前宕机了,会丢失数据。

主线程写磁盘压力大,导致写盘慢,阻塞后续操作。

1、如何实现 AOF?

AOF 日志记录 Redis 的每个写命令,步骤分为:命令追加(append)、文件写入(write)和文件同步(sync)。

命令追加 当 AOF 持久化功能打开了,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区。

文件写入和同步 关于何时将 aof_buf 缓冲区的内容写入 AOF 文件中,Redis 提供了三种写回策略:

Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;

Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;

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

2、redis.conf 中配置 AOF

默认情况下,Redis 是没有开启 AOF 的,可以通过配置 redis.conf 文件来开启 AOF 持久化,关于 AOF 的配置如下:

# appendonly 参数开启 AOF 持久化
appendonly no
# AOF 持久化的文件名,默认是 appendonly.aof
appendfilename  appendonly.aof 
# AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的
dir ./
#  同步策略
# appendfsync always
appendfsync everysec
# appendfsync no
# aof 重写期间是否同步
no-appendfsync-on-rewrite no
#  重写触发配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
#  加载 aof 出错如何处理
aof-load-truncated yes
#  文件重写策略
aof-rewrite-incremental-fsync yes

以下是 Redis 中关于 AOF 的主要配置信息:
appendfsync:这个参数项是 AOF 功能最重要的设置项之一,主要用于设置“真正执行”操作命令向 AOF 文件中同步的策略。

什么叫“真正执行”呢?还记得 Linux 操作系统对磁盘设备的操作方式吗?为了保证操作系统中 I / O 队列的操作效率,应用程序提交的 I / O 操作请求一般是被放置在 linux Page Cache 中的,然后再由 Linux 操作系统中的策略自行决定正在写到磁盘上的时机。而 Redis 中有一个 fsync() 函数,可以将 Page Cache 中待写的数据真正写入到物理设备上,而缺点是频繁调用这个 fsync() 函数干预操作系统的既定策略,可能导致 I / O 卡顿的现象频繁。

与上节对应,appendfsync 参数项可以设置三个值,分别是:always、everysec、no,默认的值为 everysec。

no-appendfsync-on-rewrite:always 和 everysec 的设置会使真正的 I / O 操作高频度的出现,甚至会出现长时间的卡顿情况,这个问题出现在操作系统层面上,所有靠工作在操作系统之上的 Redis 是没法解决的。为了尽量缓解这个情况,Redis 提供了这个设置项,保证在完成 fsync 函数调用时,不会将这段时间内发生的命令操作放入操作系统的 Page Cache(这段时间 Redis 还在接受客户端的各种写操作命令)。

auto-aof-rewrite-percentage:上文说到在生产环境下,技术人员不可能随时随地使用“BGREWRITEAOF”命令去重写 AOF 文件。所以更多时候我们需要依靠 Redis 中对 AOF 文件的自动重写策略。Redis 中对触发自动重写 AOF 文件的操作提供了两个设置:
auto-aof-rewrite-percentage 表示如果当前 AOF 文件的大小超过了上次重写后 AOF 文件的百分之多少后,就再次开始重写 AOF 文件。例如该参数值的默认设置值为 100,意思就是如果 AOF 文件的大小超过上次 AOF 文件重写后的 1 倍,就启动重写操作。
auto-aof-rewrite-min-size:设置项表示启动 AOF 文件重写操作的 AOF 文件最小大小。如果 AOF 文件大小低于这个值,则不会触发重写操作。注意,auto-aof-rewrite-percentage 和 auto-aof-rewrite-min-size 只是用来控制 Redis 中自动对 AOF 文件进行重写的情况,如果是技术人员手动调用“BGREWRITEAOF”命令,则不受这两个限制条件左右。

3、深入理解 AOF 重写

AOF 会记录每个写命令到 AOF 文件,随着时间越来越长,AOF 文件会变得越来越大。如果不加以控制,会对 Redis 服务器,甚至对操作系统造成影响,而且 AOF 文件越大,数据恢复也越慢。为了解决 AOF 文件体积膨胀的问题,Redis 提供 AOF 文件重写机制来对 AOF 文件进行“瘦身”。

图例解释 AOF 重写

AOF 重写会阻塞吗?
AOF 重写过程是由后台进程 bgrewriteaof 来完成的。主线程 fork 出后台的 bgrewriteaof 子进程,fork 会把主线程的内存拷贝一份给 bgrewriteaof 子进程,这里面就包含了数据库的最新数据。然后,bgrewriteaof 子进程就可以在不影响主线程的情况下,逐一把拷贝的数据写成操作,记入重写日志。所以 aof 在重写时,在 fork 进程时是会阻塞住主线程的。

AOF 日志何时会重写?
有两个配置项控制 AOF 重写的触发:
auto-aof-rewrite-min-size: 表示运行 AOF 重写时文件的最小大小,默认为 64MB。
auto-aof-rewrite-percentage: 这个值的计算方式是,当前 aof 文件大小和上一次重写后 aof 文件大小的差值,再除以上一次重写后 aof 文件大小。也就是当前 aof 文件比上一次重写后 aof 文件的增量大小,和上一次重写后 aof 文件大小的比值。

重写日志时,有新数据写入咋整?
重写过程总结为:“一个拷贝,两处日志”。在 fork 出子进程时的拷贝,以及在重写时,如果有新数据写入,主线程就会将命令记录到两个 aof 日志内存缓冲区中。如果 AOF 写回策略配置的是 always,则直接将命令写回旧的日志文件,并且保存一份命令至 AOF 重写缓冲区,这些操作对新的日志文件是不存在影响的。(旧的日志文件:主线程使用的日志文件,新的日志文件:bgrewriteaof 进程使用的日志文件)

而在 bgrewriteaof 子进程完成日志文件的重写操作后,会提示主线程已经完成重写操作,主线程会将 AOF 重写缓冲中的命令追加到新的日志文件后面。这时候在高并发的情况下,AOF 重写缓冲区积累可能会很大,这样就会造成阻塞,Redis 后来通过 Linux 管道技术让 aof 重写期间就能同时进行回放,这样 aof 重写结束后只需回放少量剩余的数据即可。最后通过修改文件名的方式,保证文件切换的原子性。

在 AOF 重写日志期间发生宕机的话,因为日志文件还没切换,所以恢复数据时,用的还是旧的日志文件。

总结操作:

主线程 fork 出子进程重写 aof 日志

子进程重写日志完成后,主线程追加 aof 日志缓冲

替换日志文件

温馨提示

这里的进程和线程的概念有点混乱。因为后台的 bgreweiteaof 进程就只有一个线程在操作,而主线程是 Redis 的操作进程,也是单独一个线程。这里想表达的是 Redis 主进程在 fork 出一个后台进程之后,后台进程的操作和主进程是没有任何关联的,也不会阻塞主线程

主线程 fork 出子进程是如何复制内存数据的?
fork 采用操作系统提供的写时复制(copy on write)机制,就是为了避免一次性拷贝大量内存数据给子进程造成阻塞。fork 子进程时,子进程时会拷贝父进程的页表,即虚实映射关系(虚拟内存和物理内存的映射索引表),而不会拷贝物理内存。这个拷贝会消耗大量 cpu 资源,并且拷贝完成前会阻塞主线程,阻塞时间取决于内存中的数据量,数据量越大,则内存页表越大。拷贝完成后,父子进程使用相同的内存地址空间。

但主进程是可以有数据写入的,这时候就会拷贝物理内存中的数据。如下图(进程 1 看做是主进程,进程 2 看做是子进程):

在主进程有数据写入时,而这个数据刚好在页 c 中,操作系统会创建这个页面的副本(页 c 的副本),即拷贝当前页的物理数据,将其映射到主进程中,而子进程还是使用原来的的页 c。

在重写日志整个过程时,主线程有哪些地方会被阻塞?

fork 子进程时,需要拷贝虚拟页表,会对主线程阻塞。

主进程有 bigkey 写入时,操作系统会创建页面的副本,并拷贝原有的数据,会对主线程阻塞。

子进程重写日志完成后,主进程追加 aof 重写缓冲区时可能会对主线程阻塞。

为什么 AOF 重写不复用原 AOF 日志?

父子进程写同一个文件会产生竞争问题,影响父进程的性能。

如果 AOF 重写过程中失败了,相当于污染了原本的 AOF 文件,无法做恢复数据使用。

三、RDB 和 AOF 混合方式(4.0 版本 )

Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。

这样一来,快照不用很频繁地执行,这就避免了频繁 fork 对主线程的影响。而且,AOF 日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。

如下图所示,T1 和 T2 时刻的修改,用 AOF 日志记录,等到第二次做全量快照时,就可以清空 AOF 日志,因为此时的修改都已经记录到快照中了,恢复时就不再用日志了。
怎么掌握 Redis 持久化 RDB 和 AOF
这个方法既能享受到 RDB 文件快速恢复的好处,又能享受到 AOF 只记录操作命令的简单优势, 实际环境中用的很多。

四、从持久化中恢复数据

数据的备份、持久化做完了,我们如何从这些持久化文件中恢复数据呢?如果一台服务器上有既有 RDB 文件,又有 AOF 文件,该加载谁呢?

其实想要从这些文件中恢复数据,只需要重新启动 Redis 即可。我们还是通过图来了解这个流程:
怎么掌握 Redis 持久化 RDB 和 AOF

redis 重启时判断是否开启 aof,如果开启了 aof,那么就优先加载 aof 文件;

如果 aof 存在,那么就去加载 aof 文件,加载成功的话 redis 重启成功,如果 aof 文件加载失败,那么会打印日志表示启动失败,此时可以去修复 aof 文件后重新启动;

若 aof 文件不存在,那么 redis 就会转而去加载 rdb 文件,如果 rdb 文件不存在,redis 直接启动成功;

如果 rdb 文件存在就会去加载 rdb 文件恢复数据,如加载失败则打印日志提示启动失败,如加载成功,那么 redis 重启成功,且使用 rdb 文件恢复数据;

那么为什么会优先加载 AOF 呢?因为 AOF 保存的数据更完整,通过上面的分析我们知道 AOF 基本上最多损失 1s 的数据。

“怎么掌握 Redis 持久化 RDB 和 AOF”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注丸趣 TV 网站,丸趣 TV 小编将为大家输出更多高质量的实用文章!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-13发表,共计8484字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)