12 DB的打开
先分析LevelDB是如何打开db的,万物始于创建。在打开流程中有几个辅助函数:DBImpl(),DBImpl::Recover, DBImpl::DeleteObsoleteFiles, DBImpl::RecoverLogFile, DBImpl::MaybeScheduleCompaction。
12.1 DB::Open()
函数声明为:
Status DB::Open(const Options& options, const std::string&dbname, DB** dbptr);
分解来看,Open()函数主要有以下5个执行步骤。
S1 创建DBImpl对象,其后进入DBImpl::Recover()函数执行S2和S3。
S2 从已存在的db文件恢复db数据,根据CURRENT记录的MANIFEST文件读取db元信息;这通过调用VersionSet::Recover()完成。
S3 然后过滤出那些最近的更新log,前一个版本可能新加了这些log,但并没有记录在MANIFEST中。然后依次根据时间顺序,调用DBImpl::RecoverLogFile()从旧到新回放这些操作log。回放log时可能会修改db元信息,比如dump了新的level 0文件,因此它将返回一个VersionEdit对象,记录db元信息的变动。
S4 如果DBImpl::Recover()返回成功,就执行VersionSet::LogAndApply()应用VersionEdit,并保存当前的DB信息到新的MANIFEST文件中。
S5 最后删除一些过期文件,并检查是否需要执行compaction,如果需要,就启动后台线程执行。
下面就来具体分析Open函数的代码,在Open函数中涉及到上面的3个流程。
S1 首先创建DBImpl对象,锁定并试图做Recover操作。Recover操作用来处理创建flag,比如存在就返回失败等等,尝试从已存在的sstable文件恢复db。并返回db元信息的变动信息,一个VersionEdit对象。
DBImpl* impl = newDBImpl(options, dbname);
impl->mutex_.Lock(); // 锁db
VersionEdit edit;
Status s =impl->Recover(&edit); // 处理flag&恢复:create_if_missing,error_if_exists
S2 如果Recover返回成功,则调用VersionSet取得新的log文件编号——实际上是在当前基础上+1,准备新的log文件。如果log文件创建成功,则根据log文件创建log::Writer。然后执行VersionSet::LogAndApply,根据edit记录的增量变动生成新的current version,并写入MANIFEST文件。
函数NewFileNumber(){returnnext_file_number_++;},直接返回next_file_number_。
uint64_t new_log_number =impl->versions_->NewFileNumber();
WritableFile* lfile;
s =options.env->NewWritableFile(LogFileName(dbname, new_log_number),&lfile);
if (s.ok()) {
edit.SetLogNumber(new_log_number);
impl->logfile_ = lfile;
impl->logfile_number_ =new_log_number;
impl->log_ = newlog::Writer(lfile);
s =impl->versions_->LogAndApply(&edit, &impl->mutex_);
}
S3 如果VersionSet::LogAndApply返回成功,则删除过期文件,检查是否需要执行compaction,最终返回创建的DBImpl对象。
if (s.ok()) {
i