Posted in

面试官:redis 中 rdb、aof 都有什么作用?_AI阅读总结 — 包阅AI

包阅导读总结

1. 关键词:Redis、RDB、AOF、持久化、数据恢复

2. 总结:

– 本文介绍了 Redis 的持久化功能,包括 RDB 和 AOF 两种方式。

– 详细阐述了它们的触发场景、执行过程、优缺点、配置等。

– 说明了在数据恢复和主从复制中的应用,以及混合模式和相关优化。

3. 主要内容:

– Redis 持久化

– 背景:Redis 宕机/重启内存数据易丢失,需要持久化恢复数据。

– 方式:RDB 和 AOF。

– RDB

– 触发场景:主动(save、bgsave)和自动(配置、主从复制等)。

– 执行过程:创建临时快照文件、复制数据、替换旧文件。

– 常用配置:save 配置、stop-writes-on-bgsave-error 等。

– 优缺点:速度快、资源消耗小,但数据完整性有局限。

– AOF

– 触发场景:appendonly 配置为 yes。

– 写入磁盘策略:always、everysec、no 三种。

– 重写:帮助瘦身 AOF 文件。

– 常用配置:appendonly、appendfilename 等。

– 优缺点:数据完整性好,但恢复慢、资源消耗多。

– 应用场景

– 数据恢复:加载顺序,4.0 版本的混合模式。

– 主从复制:第一次全量同步、增量同步、断开重连、无盘复制。

– 细节点:有盘复制时从节点同步命令的处理,缓冲区的优化。

思维导图:

文章地址:https://juejin.cn/post/7410220431821979648

文章来源:juejin.cn

作者:想打游戏的程序猿

发布时间:2024/9/3 13:02

语言:中文

总字数:4579字

预计阅读时间:19分钟

评分:87分

标签:Redis,持久化,RDB,AOF,主从复制


以下为原文内容

本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com

介绍

对于目前线上Redis内存几十G上百G都很常见,如果redis突然宕机/重启,内存中数据会全部丢失;直接从底层数据库中查询并写到redis,将会对底层数据库产生非常大的压力。此时就需要redis自己就能把数据恢复,这就是redis的持久化功能,redis提供了两种持久化方式:

RDB:将某一时刻内存中全部数据保存到磁盘的RDB文件中;
AOF:每当redis服务接收到写命令时,就在写完数据后以追加写的形式写到AOF缓冲区中。

还是老样子,带着问题来看本文:

  1. rdb和aof对比有什么优缺点?
  2. redis恢复数据时加载RDB、AOF的顺序是什么?
  3. redis如何实现混合使用rbd和aof的?
  4. bgsave的时候为什么主节点可以写数据并且不影响从节点的生成rdb文件?
  5. 主从同步时,如果多个从节点同时发起请求,主节点会多次bgsave么?

RDB/AOF解析

RDB

RDB(Redis Database)是在某一个时间点创建内存中数据的快照并将其保存到磁盘上的一个二进制文件的过程。

触发场景

RDB可以主动触发也可以自动触发:
主动触发:

  • save:使用save触发会阻塞主线程;
  • bgsave:调用glibc函数fork出一个子进程用于写入临时文件RDB,RDB的持久化完全交给子进程处理,在此期间父进程仍然可以处理请求,只有fork阶段会阻塞时间很短。

自动触发:

  • 在redis.conf中配置save m n:它的含义是在m秒内至少有n个key更改,则自动触发bgsave;
  • 主从复制:从节点需要从主节点进行全量复制时会触发bgsave操作,并把生成的rdb文件发送给从节点;
  • 执行debug reload命令:会重新加载redis进而触发bgsave命令;
  • 执行shutsown命令:如果此时没有开启aof,则会触发bgsave命令。

RDB执行过程

1、 创建临时快照文件:fork出一个子进程,子进程创建一个新的RDB文件,用于存储当前数据集的快照(如果执行的是save命令,则由主进程自己进行这步操作);
2、 复制数据:Redis将内存中的数据复制到临时文件中,这个过程中主进程有数据写入会采用COW(写时复制)方式;如下图:
3、 替换旧的RDB 文件:子进程写入完成,替换原有的RDB文件,并删除原有RDB文件。

Copy On Write

fork阶段不会真正的把数据复制一份给子线程,仅仅是主线程把内存空间共享给子线程,所以才能做到时间很短如图:

image.png

在共享完内存页后,kernel会把这些内存页都设置为read-only权限,当都是读数据时并不会有什么问题,一旦主线程写数据,例如写到page4中,并且检测到page4是read-only权限,此时就会复制出来一份再写进去;如图:

image.png

由于redis是读多写少,对于cow还是很适合的。

RDB常用配置

save m n : 设置RDB快照的触发条件。只有当指定的时间间隔内数据变化次数达到指定的阈值时,才会生成新的 RDB 文件。例如,save 900 1表示如果 15 分钟内至少有 1 次数据变化,就会生成一个新的 RDB 文件;
stop-writes-on-bgsave-error yes/no:控制当RDB操作失败时,是否停止写操作。默认情况下,如果RDB操作失败,Redis会停止接受写命令,以防止数据不一致;
rdbcompression yes/no:控制RDB文件是否进行压缩。默认情况下,RDB 文件会被压缩,以减少磁盘空间的占用;
rdbchecksum yes/no:控制 RDB 文件是否包含校验和。默认情况下,RDB 文件会包含校验和,以确保数据的完整性。

RDB 的优缺点

优点

  • 速度快:RDB文件是一个紧凑的二进制文件,生成和加载速度相对较快。
  • 资源消耗小:RDB操作不会阻塞主线程,对性能的影响较小。

缺点

  • 数据完整性:RDB 只能保存最后一次快照的数据,如果在两次快照之间发生故障,这段时间内的数据会丢失。

RDB更适用于需要快速恢复大量数据并且不需要高数据完整性的场景。

AOF

AOF(Append Only File)是用记录服务器接收的每个写操作,它采用的是写后日志,默认是关闭的。

触发场景

它仅有一种触发场景,appendonly配置为yes

详细内容

它的日志格式也非常简单,例如执行一条set key value命令,会以追加写的形式记录为:

*3$3set$3key$5value

含义:

  • *3:当前命令分为3部分,每部分以$+数字开发,后面紧跟着命令;
  • 数字:数字:数字:代表标识符,数字代表这部分命令的字节长度,例如$3就代表紧跟着的命令长度为3字节;

写入磁盘策略

如果每次写aof文件都直接写到磁盘中,性能会受到一定影响,redis只是把内容数据写到了内存缓冲区中,根据一定策略刷到磁盘上;redis提供了三种刷盘策略:

  • always:同步刷盘,每次写完aof_buf缓冲区中后立刻刷盘到aof文件中;
  • everysec:每秒刷盘一次;
  • no:什么时候从aof_buf缓冲区刷到磁盘由系统控制。

aof重写

由于开启aof后就会把每次写操作都记录到aof文件中,时间久了aof文件就会特别大,并且如果开启了aof,redis恢复数据时还会优先加载aof进行数据恢复,会导致恢复时间很慢;此时redis提供了bgrewriteaof能力来帮助我们给aof文件瘦身。

这里解释一下,例如执行set A v1,set A v2,set A v3,此时aof中会存在3条命令,bgrewriteaof后就只会存在一条set A v3

它的执行过程其实和bgsave很相似;

1、fork出来一个子进程,将数据转换成命令存储到新的aof文件中;
2、主线程执行过程中的写操作记录追加到这个新aof文件后尾;
3、再替换掉旧的aof文件,并清除原有aof文件。

AOF常用配置

appendonly yes:是否开启AOF持久化,默认是关闭的;
appendfilename “appendonly.aof”:生成AOF文件的名字;
appendfsync everysec:AOF文件同步策略;
auto-aof-rewrite-percentage 100:如果当前AOF文件大小超过上次重写后AOF文件大小的100%则开始重写;
auto-aof-rewrite-min-size 64mb:重写的最小文件大小。

AOF优缺点

优点

  • 数据完整性:AOF 提供了更高的数据完整性,因为它记录了每一个写操作。

缺点

  • 恢复数据慢:AOF文件可能很大,恢复速度较慢;
  • 资源消耗多:每次写操作都需要写一下aof_buf甚至刷盘;

AOF更适用于对数据丢失容忍度较低的场景。

应用场景

经过上面介绍,我们了解了RDB和AOF,他们在redis中除了数据恢复,还可以用来做主从复制。

数据恢复

对于redis上开启RDB/AOF时,redis启动加载的顺序如图:image.png

根据上面了解可以知道开启aof后采用aof文件恢复速度会很慢,所以在4.0版本提供了混合模式,通过aof-use-rdb-preamble yes开启,这样aof文件前面的内容是rdb文件,整体流程如下:

1、每次bgsave都会产生RDB文件;
2、当发生AOF重写时(bgrewriteaof),fork出一个子进程将rdb文件中的数据写到新的AOF文件中;
3、再将aof_buf的增量命令数据(aof_rewrite_buf_blocks)追加写到新的AOF文件中;
4、最后将这个前半部分是RDB后半部分是AOF新的AOF文件替换掉旧的AOF文件。

主从复制

主从复制分为四种情况:

  • master和slave第一次全量同步;
  • master和slave正常运行期间的增量同步;
  • master和slave网络断开重连数据同步;
  • 无盘复制。

master和slave第一次全量同步

下面是我从网上找来的一张同步流程图:

image.png

第一阶段:主要是建立master和slave之间的链接,从节点通过salveof 127.0.0.1 6379命令配置主节点ip和端口就可以找到master节点,并发送psync runId offset命令通知主节点我要进行数据同步,其中runId是主节点的编号,因为第一次同步不知道主节点runId所以是?,第一次同步offset是-1;主节点接收到会返回runId和offset。
第二阶段:master节点执行bgsave命令生成rdb文件,并将文件发送给从节点,从节点接收到rdb文件后将数据清空开始加载rdb文件,在这期间master接受的写命令都会缓存起来,从节点加载完rdb文件后,master会把缓存起来的命令发送给从节点。这个阶段有很多注意事项,下面详细描述;

增量同步

第三阶段:前面两个阶段就已经实现了第一次的全量同步,后续master节点处理完任务都会通过replication_buffer缓冲区发送给从节点,master会为每个从节点都维护一个缓冲区。

断开重连

在2.8版本之前每次断开重连,都会按照重跑一遍上述步骤,但对于网络抖动断开很短的时间完全没必要重新跑一遍上述步骤,只需要继续增量同步即可。在2.8版本redis对这里进行了优化,redis内部维护了一个repl_backlog,断开重连时会根据offset在repl_backlog中查找对应位置,从对应位置上开始同步,repl_backlog是一个环形缓冲区,当offset已经在repl_backlog不存在时只能选择全量同步了。通过repl-backlog-size 256mb来设置环形缓冲区的大小。

无盘复制

在上述全量复制时,主节点会先生成RDB文件到磁盘,然后在发送RDB文件到从节点,从节点接收也会先落到磁盘,再进行加载数据。此时会产生2次落盘和读盘,如果磁盘性能跟不上,同步的时间就会很长,此时就产生了无盘复制。
开启无盘复制时Redis主节点创建的新进程会直接将RDB文件写入副本的套接字,完全不涉及磁盘操作。以下是无盘复制的参数:

repl-diskless-sync yes:开启无盘复制,默认是no;
repl-diskless-sync-delay 5:等待一个时间(单位秒)再开始无盘复制,对于多个从节点无盘复制可以是并行的(有盘是串行的),如果能一起复制,就会省去master重复整理内存数据的过程;
repl-diskless-sync-max-replicas 0:当达到配置数量时,直接开启无盘复制不需要等待repl-diskless-sync-delay秒,默认为0含义是没有上限;
repl-diskless-load disabled:这是从节点的配置参数,默认为disabled代表不使用无盘复制;swapdb代表不需要清空本身数据,直接加载到内存并且解析套接字内存进行加载,此时会比较消耗内存,理论上需要预留出一倍的空闲内容;on-empty-db代表只有当前从节点没有数据时才使用无盘复制。

redis细节点

  • 有盘复制时多个从节点一起发起同步命令时,会先执行一个从节点的同步命令,其他从节点同步命令会排队,其他从节点会在当前从节点命令完成RDB文件生成后立刻使用这个文件,省去了多次生成RDB的过程;
  • 主节点为每个从节点都维护一个replication_buffer,并且自身还有一个repl_backlog,每次写数据时需要给每个缓冲区都写,这样开销就比较大。在7.0版本中采用了共享缓冲区来解决这种重复保存的问题,redis会生成多个16KB的replBufBlock,并且使用链表维护起来,每次写满16KB都会产生新的replBufBlock并连在链表最后面;每个主节点为从节点维护的缓冲区就可以根据位置ref_repl_buf_node指向各个replBufBlock;这样就省去了为每个从节点都维护一个数据缓冲区。同时repl_backlog这个环形缓冲区也复用了这个replBufBlock链表

总结

看完本篇文章相信大家能轻松应对开篇提及的这些问题;感兴趣的可以自己搭建一个redis集群根据参数手动操作一下,操作完的印象才会更深刻。


创作不易,觉得文章写得不错帮忙点个赞吧!如转载请标明出处~