rdb文件是redis实现持久化方式的一种,即通过save或bgsave操作,执行数据备份,生成的.rdb后缀的数据文件。
save和bgsave都是调用rdbSave实现备份的,只不过save是阻塞式,bgsave是非阻塞式,通过fork子进程执行备份,主进程可以继续接收外部请求。源码如下:
一、save方法
直接调用rdbsave()方法
void saveCommand(client *c) { if (server.rdb_child_pid != -1) { addReplyError(c,"Background save already in progress"); return; } rdbSaveInfo rsi, *rsiptr; rsiptr = rdbPopulateSaveInfo(&rsi); if (rdbSave(server.rdb_filename,rsiptr) == C_OK) { addReply(c,shared.ok); } else { addReply(c,shared.err); } }
二、bgsave方法
调用的是rdbSaveBackground()方法
/* BGSAVE [SCHEDULE] */ void bgsaveCommand(client *c) { int schedule = 0; /* The SCHEDULE option changes the behavior of BGSAVE when an AOF rewrite * is in progress. Instead of returning an error a BGSAVE gets scheduled. */ if (c->argc > 1) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"schedule")) { schedule = 1; } else { addReply(c,shared.syntaxerr); return; } } rdbSaveInfo rsi, *rsiptr; rsiptr = rdbPopulateSaveInfo(&rsi); if (server.rdb_child_pid != -1) { addReplyError(c,"Background save already in progress"); } else if (server.aof_child_pid != -1) { if (schedule) { server.rdb_bgsave_scheduled = 1; addReplyStatus(c,"Background saving scheduled"); } else { addReplyError(c, "An AOF log rewriting in progress: can't BGSAVE right now. " "Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenever " "possible."); } } else if (rdbSaveBackground(server.rdb_filename,rsiptr) == C_OK) { #实际上调用的是rdbSaveBackground方法 addReplyStatus(c,"Background saving started"); } else { addReply(c,shared.err); } }
三、rdbSaveBackground方法
int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) { pid_t childpid; long long start; if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR; server.dirty_before_bgsave = server.dirty; server.lastbgsave_try = time(NULL); openChildInfoPipe(); start = ustime(); if ((childpid = fork()) == 0) { int retval; /* Child */ closeListeningSockets(0); redisSetProcTitle("redis-rdb-bgsave"); retval = rdbSave(filename,rsi); # 最终调用的还是rdbsave()方法,但是是在子进程中调用的 if (retval == C_OK) { size_t private_dirty = zmalloc_get_private_dirty(-1); if (private_dirty) { serverLog(LL_NOTICE, "RDB: %zu MB of memory used by copy-on-write", private_dirty/(1024*1024)); } server.child_info_data.cow_size = private_dirty; sendChildInfo(CHILD_INFO_TYPE_RDB); } exitFromChild((retval == C_OK) ? 0 : 1); } else { /* Parent */ server.stat_fork_time = ustime()-start; server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */ latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000); if (childpid == -1) { closeChildInfoPipe(); server.lastbgsave_status = C_ERR; serverLog(LL_WARNING,"Can't save in background: fork: %s", strerror(errno)); return C_ERR; } serverLog(LL_NOTICE,"Background saving started by pid %d",childpid); server.rdb_save_time_start = time(NULL); server.rdb_child_pid = childpid; server.rdb_child_type = RDB_CHILD_TYPE_DISK; updateDictResizePolicy(); return C_OK; } return C_OK; /* unreached */ }
在了解rdb文件是怎么生成后,接下来就看看rdb文件内容,rdb文件是二进制格式的,直接打开是乱码,可以使用命令 od -c xxx.rdb 查看
四、RDB文件内容解析
本地先安装redis吧(此处省略)
由于是之前安装好的,此处执行flushall命令情况redis数据库
接下来执行save,默认是dump.rdb文件,文件名在redis.window.conf里配置,
conf文件配置
# The filename where to dump the DB dbfilename dump.rdb
使用od -c 查看
RDB文件结构包括REDIS、rdb_version、databases、EOF以及check_sum,databases部分包含SELECTDB、db_number、key_value_pairs,如下图:
其中: REDIS:是证明rdb文件,5字节 0006:版本号,1字节 377:EOF, 377之后的是checknum 因为无数据,所以也没有databases信息
再看一个有数据的rdb文件
其中: REDIS:是证明rdb文件,5字节 0006:版本号,1字节 376:SELECTDB :0号数据库,默认有16个数据库,总数可在配置中修改,databases 16 :是数据类型type,0是string 004:数据长度 name:是字符串key 004:值长度 jack:key的值 377:EOF 377后面的是checknum