leveldb将数据库的有关操作都定义在了DB类,它负责整个系统功能组件的连接和调用,是整个系统的脊柱。
level::DB是一个接口类,真正的实现在DBimpl类。
作者在文档impl.html中描述了leveldb的实现,其中包括文件组织、compaction和recovery等等。
DBimpl的成员变量包括:字符比较器internal_comparator_、配置类options_、bool型状态量、string型DB库名、cache对象、memtable对象、versionset对象等等前面所说的组件。
前面的讲解组件部分时,分散地介绍过leveldb的文件系统。这里下面来统一说明下创建一个DB,会在硬盘里生成一些什么样的文件,以下翻译自impl.html:
1 dbname/[0-9]+.log:
log文件包含了最新的db更新,每个entry更新都以append的方式追加到文件结尾。
2 dbname/[0-9]+.sst:db的sstable文件
Leveldb把sstable文件通过level的方式组织起来,从log文件中生成的sstable被放在level 0。当level 0的sstable文件个数超过设置时,leveldb就把所有的level 0文件,以及有重合的level 1文件merge起来,组织成一个新的level 1文件。
3 dbname/MANIFEST-[0-9]+:DB元信息文件
它记录的是leveldb的元信息,比如DB使用的Comparator名,以及各SSTable文件的管理信息:如Level层数、文件名、最小key和最大key等等。
4 dbname/CURRENT:记录当前正在使用的Manifest文件
它的内容就是当前的manifest文件名;因为在LevleDb的运行过程中,随着Compaction的进行,新的SSTable文件被产生,老的文件被废弃。并生成新的Manifest文件来记载sstable的变动,而CURRENT则用来记录我们关心的Manifest文件。
5 dbname/log:系统的运行日志,和options_.info_log有关,记录系统的运行信息或者错误日志。
主要函数:
Options SanitizeOptions(const std::string& dbname,
const InternalKeyComparator* icmp,
const InternalFilterPolicy* ipolicy,
const Options& src)
option修正函数,将用户定义的option做一定的检查和修正,返回规范的option对象。主要就是设置字符比较器,检查一些参数的设置(比如最大文件大小、写缓冲区的大小,sstable的block大小是否在规定值范围内)、建立log文件等等。
Status DBImpl::NewDB() { VersionEdit new_db; new_db.SetComparatorName(user_comparator()->Name()); new_db.SetLogNumber(0); new_db.SetNextFile(2); new_db.SetLastSequence(0); const std::string manifest = DescriptorFileName(dbname_, 1); WritableFile* file; Status s = env_->NewWritableFile(manifest, &file); if (!s.ok()) { return s; } { log::Writer log(file); std::string record; new_db.EncodeTo(&record); s = log.AddRecord(record); if (s.ok()) { s = file->Close(); } } delete file; if (s.ok()) { // Make "CURRENT" file that points to the new manifest file. s = SetCurrentFile(env_, dbname_, 1); } else { env_->DeleteFile(manifest); } return s; }
初始化一个新的DB对象,主要创建一个manfest文件,并调用versionedit::encodeto写入新db的信息(如comparator,lognumder,nextfilenumber,sstable信息),此函数在open()操作中被调用,完成创建DB的一步。
void DBImpl::DeleteObsoleteFiles()
根据i节点删除db中的文件,会对文件的类型和内容做一个判断,首先,正在compact的sstable不删,versionset中各个版本下的sstable文件不删,当前的log和manfest文件不删。调用env_->DeleteFile删除文件。
Status DBImpl::Recover(VersionEdit* edit)
DB恢复函数,基于前面介绍的文件系统
1.recover首先找到当前数据库dbname_路径下的current文件,参考函数CurrentFileName(dbname_),文件错误或者不存在,恢复都无法继续进行),2.然后调用versionset::recover(),读取manfest文件,通过一个versionedit对象中间过渡,恢复出新的version。
3.遍历dbname_文件下的文件,对比当前版本集合versions_中记录的sstable,如果缺失,输出缺失的文件i节点,recover失败,否则
恢复log文件(参考RecoverLogFile函数)
Status DBImpl::RecoverLogFile(uint64_t log_number, VersionEdit* edit, SequenceNumber* max_sequence)
从log文件中逐条恢复entry,并写入新建立的memtable。并在合适的条件下(memtable大小大于写缓存下限:mem->ApproximateMemoryUsage() > options_.write_buffer_size),写入level_0的sstable中(参考函数WriteLevel0Table)
Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base)
将memtable dump到磁盘,也就是level-0的sstable中。
1.首先产生一个新文件,并记录在文件描述结构FileMetaData中
2.利用memtable的迭代器Iterator遍历memtable中的KV数据,构造sstable(参考函数BuildTable,还记得前面介绍table和block么,要对memtable的kv做进一步的打包,才能形成kv的磁盘形式)
3.把新的文件变化信息存储进versionedit,并记录这次compact的信息,主要是耗时和写入的sstable大小。
注:PickLevelForMemTableOutput函数,新的sstabl