面试必问的 Redis:RDB、AOF、混合持久化

本来说 redis 分3篇,但是上周写持久化时发现持久化的内容还越多的,于是持久化就单拆一篇了 。
我估计后面的主从复制、哨兵、集群内容也是不少,所以说实话,我也不知道之前说的3篇会拆成几篇了
持久化机制的内容大纲其实很早就有了,但是实际写的时候断断续续写了有两周 。
主要细节还是挺多的,在翻源码的过程中,会遇到一些疑惑点,也发现一些自己以前不知道的知识点,所以自己也要花点时间去搞清楚 。
慢工出细活吧,本文还是有很多非常细节的内容的,如果能掌握,让大厂面试官眼前一亮还是问题不大的 。
 
正文
Redis 核心主流程
 
AOF 和 RDB 的持久化过程中,有不少操作是在时间事件 serverCron 中被触发的 。所以,这边有必要先了解下 Redis 中的事件核心流程 。
 
Redis 的服务器进程就是一个事件循环,最重要的有两个事件:文件事件和时间事件 。Redis 在服务器初始化后,会无限循环,处理产生的文件事件和时间事件 。
 
文件事件常见的有:接受连接(accept)、读取(read)、写入(write)、关闭连接(close)等 。
 
时间事件中常见的就是 serverCron,redis 核心流程中通常也只有这个时间事件 。serverCron 默认配置下每100ms会被触发一次,在该时间事件中,会执行很多操作:清理过期键、AOF 后台重写、RDB 的 save point 的检查、将 aof_buf 内容写到磁盘上(flushAppendOnlyFile 函数)等等 。
 
Redis 的核心主流程如下图:

面试必问的 Redis:RDB、AOF、混合持久化

文章插图
 
相关源码在 server.c、ae.c,核心方法是:main、aeProcessEvents
 
Redis 的持久化机制有哪几种
 
RDB、AOF、混合持久化(redis4.0引入)
 
RDB的实现原理、优缺点
 
描述:类似于快照 。在某个时间点,将 Redis 在内存中的数据库状态(数据库的键值对等信息)保存到磁盘里面 。RDB 持久化功能生成的 RDB 文件是经过压缩的二进制文件 。
 
命令:有两个 Redis 命令可以用于生成 RDB 文件,一个是 SAVE,另一个是 BGSAVE 。
 
开启:使用 save point 配置,满足 save point 条件后会触发 BGSAVE 来存储一次快照,这边的 save point 检查就是在上文提到的 serverCron 中进行 。
 
save point 格式:save <seconds> <changes>,含义是 Redis 如果在 seconds 秒内数据发生了 changes 次改变,就保存快照文件 。例如 Redis 默认就配置了以下3个:
save 900 1 #900秒内有1个key发生了变化,则触发保存RDB文件save 300 10 #300秒内有10个key发生了变化,则触发保存RDB文件save 60 10000 #60秒内有10000个key发生了变化,则触发保存RDB文件 
关闭:1)注释掉所有save point 配置可以关闭 RDB 持久化 。2)在所有 save point 配置后增加:save "",该配置可以删除所有之前配置的 save point 。
save "" 
SAVE:生成 RDB 快照文件,但是会阻塞主进程,服务器将无法处理客户端发来的命令请求,所以通常不会直接使用该命令 。
 
BGSAVE:fork 子进程来生成 RDB 快照文件,阻塞只会发生在 fork 子进程的时候,之后主进程可以正常处理请求,详细过程如下图:
面试必问的 Redis:RDB、AOF、混合持久化

文章插图
 
fork:在 linux 系统中,调用 fork() 时,会创建出一个新进程,称为子进程,子进程会拷贝父进程的 page table 。如果进程占用的内存越大,进程的 page table 也会越大,那么 fork 也会占用更多的时间 。如果 Redis 占用的内存很大,那么在 fork 子进程时,则会出现明显的停顿现象 。
 
RDB 的优点:
1)RDB 文件是是经过压缩的二进制文件,占用空间很小,它保存了 Redis 某个时间点的数据集,很适合用于做备份 。比如说,你可以在最近的 24 小时内,每小时备份一次 RDB 文件,并且在每个月的每一天,也备份一个 RDB 文件 。这样的话,即使遇上问题,也可以随时将数据集还原到不同的版本 。
 
2)RDB 非常适用于灾难恢复(disaster recovery):它只有一个文件,并且内容都非常紧凑,可以(在加密后)将它传送到别的数据中心 。
 
3)RDB 可以最大化 redis 的性能 。父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作 。


推荐阅读