DBImpl::Recover
把数据库恢复到上次退出的状态,
Recover的基本功能:如果存在表数据,则Load表数据,并对日志进行恢复,否则,根据flag创建新表或者返回错误
Recover的基本流程是:首先是处理创建flag,比如存在就返回失败等等;然后是尝试从已存在的sstable文件恢复db;最后如果发现有大于manifest文件记录的log编号的log文件,则需要回放log(回放的是上一次db关闭时还存在于mem和immem的记录,因为这些记录并没有持久化到磁盘sst文件中),更新db数据。回放期间db可能会dump新的level 0文件,因此需要把db元信息的变动记录到edit中返回
Status DBImpl::Recover(VersionEdit* edit, bool *save_manifest) {
mutex_.AssertHeld();
// 创建DB目录,不关注错误
env_->CreateDir(dbname_);
// 在DB目录下打开或创建(如果不存在)LOCK文件并锁定它,防止其他进程打开此表
Status s = env_->LockFile(LockFileName(dbname_), &db_lock_);
if (!s.ok()) {
return s;
}
//判断CURRENT文件是否存在,current不存在说明数据库不存在
if (!env_->FileExists(CurrentFileName(dbname_))) {
//若CURRENT文件不存在,如果options选项设置了create_if_missing,则创建新的db
if (options_.create_if_missing) {
s = NewDB();
if (!s.ok()) {
return s;
}
} else {
//否则返回db不存在
return Status::InvalidArgument(
dbname_, "does not exist (create_if_missing is false)");
}
} else {
if (options_.error_if_exists) {
//如果数据库存在且设置了error_if_exists,则返回error_if_exists错误
return Status::InvalidArgument(
dbname_, "exists (error_if_exists is true)");
}
}
// 如果运行到此,表明数据库已经存在,需要load,第一步是从MANIFEST文件中恢复VersionSet
s = versions_->Recover(save_manifest);
if (!s.ok()) {
return s;
}
SequenceNumber max_sequence(0);
/*尝试从所有比manifest文件中记录的log要新的log文件中恢复(前一个版本可能会添加新的
log文件,却没有记录在manifest中)。这种情况出现在memtable或者immemtable还没来得
及写入sst文件db就挂掉了,因此需要从比manifest文件中记录的log要新的log文件中恢复*/
//prev_log是早前版本level_db使用的机制,现在以及不再使用,这里只是为了兼容
const uint64_t min_log = versions_->LogNumber();
const uint64_t prev_log = versions_->PrevLogNumber();
std::vector<std::string> filenames;
// 列出目录内的所有文件
s = env_->GetChildren(dbname_, &filenames);
if (!s.ok()) {
return s;
}
std::set<uint64_t> expected;
// 这个函数实质是获取仍然存活(仍然有效)的文件
versions_->AddLiveFiles(&expected);
uint64_t number;
FileType type;
std::vector<uint64_t> logs;
//这里先找出所有满足条件的log文件:比manifest文件记录的log编号更新。
for (size_t i = 0; i < filenames.size(); i++) {
if (ParseFileName(filenames[i], &number, &type)) {
// 从这里删除的目的是为了最后看看还有哪些文件名是不能够解析的
expected.erase(number);
if (type == kLogFile && ((number >= min_log) || (number == prev_log)))
logs.push_back(number);
}
}
// 如果这个数组不为空,那么表示有的文件名解析不了,出错!
if (!expected.empty()) {
char b