Redis提供了RDB持久化功能,这个功能可以将Redis在内存中的数据库状态保存到磁盘里面去,避免数据意外丢失。
RDB持久化既可以手动执行,也可以根据服务器配置选项定期执行,该功能可以将某个时间点上的数据库状态保存到一个RDB文件中
RDB持久化功能所生成的RDB文件是一个经过压缩的二进制文件,通过该文件可以还原生成RDB文件时的数据库状态。
RDB文件是保存在硬盘里面的,所以即使Redis服务器进程退出,甚至运行Redis服务器的计算机停机,但只要RDB文件仍然存在,Redis服务器就可以用他来还原数据库状态。
创建(SAVE与BGSAVE)
有两个Redis命令可以用于生成RDB文件,一个是SAVE,另一个是BGSAVE。
SAVE命令会阻塞Redis服务器的进程,知道RDB文件创建完毕,在服务器进程阻塞期间,服务器不能处理任何命令请求的
//执行save命令,RDB进行持久化生成RDB文件
save
和SAVE命令直接阻塞服务器进程的做法不同,BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程继续处理命令请求。
这里涉及到两个问题?为什么是进程,而不是开线程(消耗更少资源,一个进程包含多个线程)
这是因为Redis是单线程的,不可能开多一个线程。
Redis为什么是单线程?
Redis是一个内存型数据库,数据都是从内存上读取的,所以CPU并不是他的上限瓶颈,因为CPU读内存是很快的,足够应付大流量,Redis的上限瓶颈是内存的大小,所以只需要一个单线程就足够了,不需要开多个线程去访问内存加快速度。
所以BGSAVE是派生出一个子进程去执行创建RDB文件。
创建RDB文件的实际工作是由rdb.c/rdbSave函数完成,SAVE命令和BGSAVE命令会以不同的方式去调用这个函数。下面用伪代码去表示两者不同
def SAVE():
//创建RDB文件
rdbSave()
End SAVE;
def BGSAVE():
//创建子进程
pid = fork()
if pid == 0:
//成功创建到子进程
//让子进程去创建RDB文件
rdbSave()
//完成之后向父进程发送信号
signal_parent()
elif pid > 0:
//子进程数量太多,轮询等待子进程创建成功
//父进程继续处理命令请求,并通过轮询等待子进程的信号
handle_requeset_and_wait_signal()
else:
//子进程为负数
//处理出错情况
handle_fork_error()
End BGSAVE;
载入RDB文件
RDB文件的载入工作是在服务器启动时自动执行的,所以Redis并没有专门用于载入RDB文件的命令,只要Redis服务器在启动时,只要检测到RDB文件存在,就会自动载入RDB文件。
另外值得一提的是,因为AOF文件的更新频率通常比RDB文件的更新频率高,所以:
-
如果服务器开启了AOF持久化功能,那么服务器会优先用AOF文件来还原数据库的状态
-
只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据库状态
SAVE命令执行时的服务器状态
当SAVE命令在执行时,Redis服务器会被阻塞,当SAVE命令正在进行时,客户端发送的所有命令请求都会被阻塞,直到SAVE命令执行完成,重新开始接收命令请求之后,客户端发送的命令才会被处理
BGSAVE命令执行的服务器状态
BGSAVE命令是由子进程去执行的,所以在子进程创建RDB文件的过程,并不会阻塞Redis,Redis服务器仍然会继续处理客户端的命令请求,但是,在BGSAVE命令执行期间,服务器处理SAVE、BGSAVE、BGREWRITEAOF三个命令会和平时不一样。
首先,Redis在BGSAVE命令期间,客户端发送的SAVE命令会被服务器拒绝,服务器会禁止SAVE命令和BGSAVE命令同时执行,这种操作是为了避免父进程和子进程同时执行,即会同时调用两个rdbSave,防止产生竞争条件。
其次,在BGSAVE期间也是不可以执行客户端发送的BGSAVE,因为也是会产生竞争条件。
最后的就是BGREWRITEAOF和BGSAVE两个命令是不可以同时执行的,情况分以下两种
-
如果BGSAVE命令正在执行,那么客户端发送的BGREWRITEAOF命令会被延迟到BGSAVE命令执行完毕之后执行
-
如果BGREWRITEAOF命令正在执行,那么客户端发送的BUSAVE命令会被服务器拒绝
虽然BGREWRITEAOF和BGSAVE两个命令的实际工作都是由子进程执行的,
《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》
【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享
这两个命令在操作方面并没有什么冲突的地方,但是不能同时执行他们只是一个性能方面的考虑,并发出两个子进程,并且这两个子进程都同时执行大量的磁盘写入操作,这会很消耗CPU资源,不是一个好主意(至于为什么更倾向于执行BGREWRITEAOF,可能Redis设计者觉得AOF的优先级比较高吧)。
RDB文件载入时的服务器状态
服务器在载入RDB文件期间,会一直处于阻塞状态,直到载入工作完成为止。
SAVE命令与BGSAVE命令主要的区别其实就是SAVE命令是服务器进程去执行工作的,会阻塞服务器进程影响服务,而BGSAVE是服务器进程开启了另一个进程去执行的,不会影响服务器进程。
Redis允许用户通过设置服务器配置的SAVE选项,让服务器每隔一段时间自动执行一次BGSAVE命令,而且SAVE选项可以有多个保存条件(触发执行BGSAVE命令条件),只要其中一个条件被满足,服务器就会执行BGSAVE命令
redis默认的配置如下
那么只要满足以下三个条件之一,BGSAVE命令就会被执行
-
SAVE 900 1:服务器在900秒之内(15分钟),只要服务器进行了至少一次修改(新增修改和删除键)
-
SAVE 300 10:服务器在300秒之内(5分钟),只要服务器进行了至少十次修改(新增修改和删除键)
-
SAVE 60 10000:服务器在60秒之内(1分钟),只要服务器进行了至少一万次修改(新增修改和删除键)
每次触发都会重写RDB文件(原来的数据清空,完整将数据重新放入)
也就是60秒检查一次,300秒检查一次,900秒检查一次,符合条件就会重写RDB文件,重写了之后重新计算时间
设置保存条件
Redis服务器启动后,用户可以通过指定配置文件或者传入启动参数的方式去设置save选项
接着,服务器会根据save选项所设置的保存条件,设置服务器状态redisServer对象(前面提过其存储服务器状态,里面有数据库数组)里面的saveparams属性
struct redisServer{
//。。。
。。。
//记录了保存条件的数组
struct saveparam *saveparams;
};