深入剖析Redis RDB持久化机制 (5)

Redis.c:1717

if (server.appendonly) { if (loadAppendOnlyFile(server.appendfilename) == REDIS_OK) redisLog(REDIS_NOTICE,"DB loaded from append only file: %ld seconds",time(NULL)-start); } else { if (rdbLoad(server.dbfilename) == REDIS_OK) { redisLog(REDIS_NOTICE,"DB loaded from disk: %ld seconds", time(NULL)-start); } else if (errno != ENOENT) { redisLog(REDIS_WARNING,"Fatal error loading the DB. Exiting."); exit(1); } }

如果保存了AOF文件,则使用AOF文件来恢复数据,AOF的具体内容见AOF

如果没有AOF,则使用rdb文件恢复数据,调用rdbLoad函数

接着看看rdbLoad函数

Rdb.c:929

int rdbLoad(char *filename) { ... fp = fopen(filename,"r"); if (!fp) { errno = ENOENT; return REDIS_ERR; } if (fread(buf,9,1,fp) == 0) goto eoferr; buf[9] = \'\0\'; if (memcmp(buf,"REDIS",5) != 0) { fclose(fp); redisLog(REDIS_WARNING,"Wrong signature trying to load DB from file"); errno = EINVAL; return REDIS_ERR; } rdbver = atoi(buf+5); if (rdbver < 1 || rdbver > 2) { fclose(fp); redisLog(REDIS_WARNING,"Can\'t handle RDB format version %d",rdbver); errno = EINVAL; return REDIS_ERR; } startLoading(fp); while(1) { robj *key, *val; int force_swapout; expiretime = -1; /* Serve the clients from time to time */ if (!(loops++ % 1000)) { loadingProgress(ftello(fp)); aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT); } /* Read type. */ if ((type = rdbLoadType(fp)) == -1) goto eoferr; if (type == REDIS_EXPIRETIME) { if ((expiretime = rdbLoadTime(fp)) == -1) goto eoferr; /* We read the time so we need to read the object type again */ if ((type = rdbLoadType(fp)) == -1) goto eoferr; } if (type == REDIS_EOF) break; /* Handle SELECT DB opcode as a special case */ if (type == REDIS_SELECTDB) { if ((dbid = rdbLoadLen(fp,NULL)) == REDIS_RDB_LENERR) goto eoferr; if (dbid >= (unsigned)server.dbnum) { redisLog(REDIS_WARNING,"FATAL: Data file was created with a Redis server configured to handle more than %d databases. Exiting\n", server.dbnum); exit(1); } db = server.db+dbid; continue; } /* Read key */ if ((key = rdbLoadStringObject(fp)) == NULL) goto eoferr; /* Read value */ if ((val = rdbLoadObject(type,fp)) == NULL) goto eoferr; /* Check if the key already expired. This function is used when loading * an RDB file from disk, either at startup, or when an RDB was * received from the master. In the latter case, the master is * responsible for key expiry. If we would expire keys here, the * snapshot taken by the master may not be reflected on the slave. */ if (server.masterhost == NULL && expiretime != -1 && expiretime < now) { decrRefCount(key); decrRefCount(val); continue; } /* Add the new object in the hash table */ dbAdd(db,key,val); /* Set the expire time if needed */ if (expiretime != -1) setExpire(db,key,expiretime); /* Handle swapping while loading big datasets when VM is on */ /* If we detecter we are hopeless about fitting something in memory * we just swap every new key on disk. Directly… * Note that\'s important to check for this condition before resorting * to random sampling, otherwise we may try to swap already * swapped keys. */ if (swap_all_values) { dictEntry *de = dictFind(db->dict,key->ptr); /* de may be NULL since the key already expired */ if (de) { vmpointer *vp; val = dictGetEntryVal(de); if (val->refcount == 1 && (vp = vmSwapObjectBlocking(val)) != NULL) dictGetEntryVal(de) = vp; } decrRefCount(key); continue; } decrRefCount(key); /* Flush data on disk once 32 MB of additional RAM are used… */ force_swapout = 0; if ((zmalloc_used_memory() - server.vm_max_memory) > 1024*1024*32) force_swapout = 1; /* If we have still some hope of having some value fitting memory * then we try random sampling. */ if (!swap_all_values && server.vm_enabled && force_swapout) { while (zmalloc_used_memory() > server.vm_max_memory) { if (vmSwapOneObjectBlocking() == REDIS_ERR) break; } if (zmalloc_used_memory() > server.vm_max_memory) swap_all_values = 1; /* We are already using too much mem */ } } fclose(fp); stopLoading(); return REDIS_OK; eoferr: /* unexpected end of file is handled here with a fatal exit */ redisLog(REDIS_WARNING,"Short read or OOM loading DB. Unrecoverable error, aborting now."); exit(1); return REDIS_ERR; /* Just to avoid warning */ }

打开rdb文件

读取rdb文件的签名和版本号

开始进入 类型 | 值 | 类型 | 值 的循环读取,可参考rdb文件格式

作者还做了导入的进度条,是有人反馈说rdb文件很大时导入时要很久,但又不知道进度,所以作者就加了导入的进度条,改善用户体验

读取类型

如果类型是过期时间类型REDIS_EXPIRETIME,则读取过期时间

如果类型是文件结束类型REDIS_EOF,则跳出 类型 | 值 | 类型 | 值 的循环读取

如果类型是选择db类型REDIS_SELECTDB,则读取db索引并把当前db转成该db,然后继续 类型 | 值 | 类型 | 值 的循环读取。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzzddp.html