redis持久化之RDB
RDB二进制文件用于还原数据库
1. RDB文件的创建和载入
SAVE命令会阻塞redis的其他进程来创建RDB文件,期间会拒绝所有命令请求; 而BGSAVE则会创建一个子进程,由子进程负责创建RDB文件,而父进程继续执行服务器接受的命令请求。
- 在执行BGSAVE命令期间,服务器会拒绝SAVE命令,原因是为了避免父进程和子进程同时执行rdbSave调用,防止产生竞争条件;
- 同样也会拒绝BGSAVE操作;
- 假设正在执行BGSAVE,客户端发送BGREWRITEAOF命令,那么该命令会延迟到BGSAVE结束之后再被调用执行(相当于阻塞);
- 如果BGREWRITEAOF正在执行,那么客户端发送BGSAVE会被服务器直接拒绝。
载入操作并没有显示的命令,而是在redis服务启动时会自动检测RDB文件是否存在,自动进行载入。(同样载入操作也是原子操作,其余命令也都会被阻塞)。 这里先提一嘴,AOF的更新频率通常比RDB高,所以如果服务器开启了AOF持久化,那么会优先使用AOF文件来还原数据库状态。
2. SAVE和BGSAVE命令
因为BGSAVE不会阻塞服务器进程,因此redis允许用户用过设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE操作。 可以设置多个保存条件,只要满足一个条件,服务器就会执行BGSAVE操作:
-
save 900 1 // 表示900秒内,对数据库进行了至少1次修改 -
save 300 10 // 表示300秒内,对数据库进行了至少10次修改 -
save 60 10000 // 表示10秒内,对数据库进行了至少10000次修改 也可以设置redisServer中的saveparams属性来自定义保存条件: struct redisServer {
struct saveparam *saveparams;
}
saveparams 是一个一维的数组,其中的每一个元素都是一个saveparam结构,该结构中设置了保存条件:
struct saveparam {
// 秒数
time_t seconds;
// 修改数
int changes;
}
dirty计数器和lastsave属性
这两个属性就是为了配合修改次数和记录间隔时间用的,应该也非常好理解。 这两个属性也是在redisServer中:
struct redisServer {
// 修改计数器
long long dirty;
// 上一次执行保存的时间
time_t lastsave;
}
redis服务器的周期性操作函数serverCron默认100毫秒会执行一次,检查save选项中所设置的保存条件是否已经满足,如果满足条件则执行BGSAVE。
def serverCron():
for saveparam in server.saveparams:
save_interval = unixtime_now()- server.lastsave
if server.dirty >= saveparams.changes and save_interval > saveparam.seconds:
BGSAVE()
3 RDB文件结构
分为 REDIS | db_version | databases | EOF | check_sum 5 部分 tips(全大写标识常量,全小写标识变量和数据)
开头部分REDIS 长度为5个字节,快速检查是否为RDB文件。 db_version 长度为4个字节,表示RDB文件的版本号,"0006"表示RDB文件的第六版。 databases 包含着零个或任意多个数据库,以及各数据库中的键值对数据: 数据库状态为空,这部分也为空,长度0字节; 数据库状态为非空,这部分也非空。 EOF常量的长度为1字节,标志着RDB文件正文内容的结束,当程序读到这个值时,表面数据库所有的键值都载入完毕。 check_num是一个8字节唱的无符号整树,保存一个校验和。是程序对REDIS、db_version、databases、EOF内容计算得出的。当服务器载入RDB文件时,会将载入的数据计算出校验和并与check_num中存放的值进行对比,来检查RDB文件是否有出错或损坏的情况出现。
|