目录
上篇文章已经大概介绍过Redis的两种持久化方式了,现在我们去深度了解一下他们是如何工作的#
RDB文件的创建和载入
Redis的RDB持久化功能可以将Redis在内存中的数据库状态保存到磁盘中去,来避免数据的丢失。
这里主要介绍Redis服务器保存和载入RDB文件的方法,重点讲解SAVE和BGSAVE这两个命令的实现方式
- rdb文件的创建
SAVE命令:
SAVE命令会阻塞Redis进程,直到rdb文件创建完毕为止,在此期间,Redis不能进行任何操作
redis>SAVE
OK
BGSAVE命令:
BGSAVE命令不会阻塞进程,而是创建一个子进程去创建RDB文件,期间可以去进行其他操作。
创建RDB文件实际工作由rdb.c or rdbSave 函数完成,伪代码如下,可以观察上面两点的区别:
def SAVE():
rdbSave()
def BGSAVE():
pid = fork(); //创建子进程,pid父进程返回子进程id,子进程返回0
if pid == 0; //子进程返回的0
rdbSave() //子进程进行RDB文件创建
signal_parent() //创建完成后向父进程发送信号
elif pid > 0: // 父进程返回的是子进程id,都是大于0的
handle_request_and_wait_signal() // 父进程继续工作,等待子进程信号
else:
handle_fork_error() //处理出错状态
2.RDB文件的载入
RDB文件的载入是自动载入的,当启动Redis服务器的时候会检测是否有RDB文件,有则自动载入。服务器自动载入会在日志打印 DB loaded from disk: 0.001 seconds
RDB文件载入过程中,服务器一直处于阻塞状态,直至载入完成。
rdbSave和rdbLoad之间的关系如下图
注意:
1》BGSAVE命令执行过程中,系统会拒绝客户端发送的SAVE命令,原因是为了避免服务器主进程和子进程同时调用rdbSave函数,防止产生竞争条件。同时两个BGSAVE也会被拒绝,因为这样也会产生竞争条件。
2》最后,BGSAVE命令和BGREWRITEAOF命令也不能同时执行。具体情况如下:
如果BGSAVE命令执行过程中收到BGREWRITEAOF命令,BGREWRITEAOF命令会被延迟到BGSAVE执行完毕后执行。如果BGREWRITEAOF命令执行过程中收到BGSAVE命令,那么BGSAVE命令会被服务器拒绝。
这种情况产生的原因:
BGREWRITEAOF和BGSAVE两个命令实际都由子进程执行,所以在命令的执行方面没有问题,但是如果并发两个子进程,并且这两个子进程都同时进行大量的磁盘写入操作,这将会占用大量资源的。
设置自动间隔性保存
上面我们提到SAVE命令会阻塞服务器,而BGSAVE不会阻塞,所以,这个任务就可以让BGSAVE来执行了。用户可以通过设置save选项来进行相关的设置,例如:
save 900 1
save 300 10
save 60 10000
意思是说,服务器在900秒、300秒、60秒之内,对数据库进行了至少1、10、10000此修改,则执行一次BGSAVE。
那么Redis是如何去存储这些save设置的呢?
答案就是saveparams属性。
struct redisServer{
//.....
struct saveparam *saveparams; //记录save条件的数组。
}
对,这里的saveparams是一个数组,数组的每个元素都是一个saveparams结构
struct saveparam{
time_t seconds; //秒数
int changes; //改变次数
}
以上面那个保存条件为例,服务器中的数组应该是这个样子的。每个数组的成员都代表一个保存条件。
说到这里,那么服务器是如何实现间歇性的呢?
这里就用到了周期性操作函数serverCron,默认每100毫秒会执行一次,主要是检查是否达到了save条件,达到了就执行BGSAVE命令。
服务器其他属性
- dirty计数器:记录上一次成功执行SAVE或BGSAVE后服务器对数据库操作的次数。
- lastsave:这是一个UNIX的时间戳,记录成功执行SAVE或BGSAVE的时间。
struct redisSserver{
//............
long long dirty; //计数器
time_t lastsave; //上次保存的时间
//...........
}
例如:
执行
redis>SET message "hello"
OK
此时dirty计数器加1,变为 101.
RDB文件的结构
上面讲了那么多对RDB文件的操作,但是没有说RDB文件本身,接下来一起了解一下RDB文件的结构吧。
RDB文件就包括这五部分
1》开头的REDIS长度为5字节(是'R''E''D''I''S'五个字符,而不是字符串),在载入RDB文件时,主要通过检查开头的这五个字符来判断是否为RDB文件
2》db_version长度为4字节,是字符串表示的一个整数,这个整数代表文件的版本号。
3》databases 表示零个或者多个数据库,以及各个数据库中的键值对数据(这个部分的长度是随数据库大小而改变的,如果无数据库,则此部分也为空,长度为0字节)
4》EOF 长度1字节,标志文件正文已经读入完毕,已读入所有数据库数据。
5》check_sum 长度8字节无符号整数,这个是用来检验RDB文件是否出错的部分,通过特殊的计算每部分的情况,以此来检查文件是否出错或者损坏。