简介
rdb用于保存redis数据的快照
优点:
1 一般时候对性能影响较小
2 非常适合做冷备
3 数据恢复快
缺点:
1 如果数据量大时候,会导致cpu和内存占用率升高,影响性能(所以实际操作中,如果开启rdb那么物理内存最好是maxmemory的两倍多)
2 可能会丢失较多数据
触发点
1 执行bgsave和save
2 满足配置的rdb策略
对应配置文件中如下:
save 900 1
save 300 10
save 60 10000
执行bgsave(save类似,只是少了fork)
struct redisCommand redisCommandTable[] = {
...
{"bgsave",bgsaveCommand,-1,"as",0,NULL,0,0,0,0,0},
...
}
void bgsaveCommand(client *c) {
int schedule = 0;
判断是否有rdb在进行中
if (server.rdb_child_pid != -1) {
addReplyError(c,"Background save already in progress");
判断是否有aof在进行中
} 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.");
}
执行rdb
} else if (rdbSaveBackground(server.rdb_filename,rsiptr) == C_OK) {
addReplyStatus(c,"Background saving started");
} else {
addReply(c,shared.err);
}
}
int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
...
if ((childpid = fork()) == 0) {
int retval;
关闭子进程复制的socket
closeListeningSockets(0);
执行保存
retval = rdbSave(filename,rsi);
}
int rdbSave(char *filename, rdbSaveInfo *rsi) {
char tmpfile[256];
建立一个临时文件
snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
fp = fopen(tmpfile,"w");
...
把文件描述符复制给rdb
rioInitWithFile(&rdb,fp);
...
设置渐进式fsync,防止一致性的大写入
if (server.rdb_save_incremental_fsync)
rioSetAutoSync(&rdb,REDIS_AUTOSYNC_BYTES);
写入
if (rdbSaveRio(&rdb,&error,RDB_SAVE_NONE,rsi) == C_ERR) {
errno = error;
goto werr;
}
重命名
if (rename(tmpfile,filename) == -1) {
...
}
}
int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
...
for (j = 0; j < server.dbnum; j++) {
写入选择的db
if (rdbSaveType(rdb,RDB_OPCODE_SELECTDB) == -1) goto werr;
if (rdbSaveLen(rdb,j) == -1) goto werr;
迭代所有键值写入
while((de = dictNext(di)) != NULL) {
sds keystr = dictGetKey(de);
robj key, *o = dictGetVal(de);
long long expire;
initStaticStringObject(key,keystr);
expire = getExpire(db,&key);
if (rdbSaveKeyValuePair(rdb,&key,o,expire) == -1) goto werr;
}
dictReleaseIterator(di);
di = NULL;
}
...
}
rdb策略
if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
serverPanic("Can't create event loop timers.")
exit(1)
}
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
for (j = 0
struct saveparam *sp = server.saveparams+j
如何我们达到了给定的变化数量在给定的秒数而且上一次bgsave成功了,或者已经过了CONFIG_BGSAVE_RETRY_DELAY的秒数
if (server.dirty >= sp->changes &&
server.unixtime-server.lastsave > sp->seconds &&
(server.unixtime-server.lastbgsave_try >
CONFIG_BGSAVE_RETRY_DELAY ||
server.lastbgsave_status == C_OK))
{
serverLog(LL_NOTICE,"%d changes in %d seconds. Saving...",
sp->changes, (int)sp->seconds)
rdbSaveInfo rsi, *rsiptr
rsiptr = rdbPopulateSaveInfo(&rsi)
rdbSaveBackground(server.rdb_filename,rsiptr)
break
}
}
...
}