Open DB使用示例leveldb::DB*db;leveldb::Options options;// 如果数据库目录不存在自动创建options.create_if_missingtrue;// 1. 打开数据库数据会持久化存储在当前目录的 testdb 文件夹中leveldb::Status statusleveldb::DB::Open(options,./testdb,db);下面看下Open函数里面的处理流程创建DBImpl对象传入option和name参数调用DBimpl对象的Recover恢复db如果当前对象的memtable为空那么创建memtable如果有修改Version那么将内容持久化到Manifest中。调用RemoveObsoleteFiles清理没有使用的文件启动Compaction。OptionsOption的定义如下structLEVELDB_EXPORTOptions{Options();constComparator*comparator// key的比较器对象boolcreate_if_missingfalse;// DB不存在时是否自动创建打开不存在的DB会报错boolerror_if_existsfalse;// DB已存在时是否报错boolparanoid_checksfalse;// 激进检查模式。开启后遇到任何数据异常立即报错停止Env*env;// 封装所有 OS 交互文件读写、线程调度、时间等Logger*info_lognullptr;// 日志输出对象。为空时LevelDB 会在数据库目录下自动创建一个日志文件size_t write_buffer_size4*1024*1024;// MemTable 的大小上限默认 4MBintmax_open_files1000;// 同时打开的最大文件数。Cache*block_cachenullptr;// Block 缓存。为nullptr时自动创建 8MB 内部缓存。size_t block_size4*1024;// SSTable 中每个 block 的未压缩数据大小默认 4KBintblock_restart_interval16;// 增量编码的重启间隔。SSTable 内部用前缀压缩存储相邻 key 的公共部分以节省空间。每 16 个 key 设一个重启点存储完整 key。建议不要修改这个参数。size_t max_file_size2*1024*1024;// 单个 SSTable 文件的大小上限默认 2MBCompressionType compressionkSnappyCompression;// 压缩算法。boolreuse_logsfalse;// 设为true时打开DB时复用已有 MANIFEST 和日志文件而非新建constFilterPolicy*filter_policynullptr;// 过滤器策略。nullptr 时不使用过滤器每次 Get 都必须真正读取 SSTable。};Recoverrecover流程如下获取文件锁文件名字为dbname “/LOCK”保证并发的正确性获取current文件dbname “/CURRENT”如果不存在这则表示db为新建需要创建新的DB对象。但是如果options_.create_if_missing为false则直接返回参数错误。创建CURRENT文件时先创建一个临时文件将内容写入后通过rename习惯临时文件的名字为CURRENT利用文件系统rename的原子性保证创建CURRENT的原子性恢复VersionSet读取current文件通过current里的内容读取Manifest文件解析Manifest中的内容恢复到VersionSet中回放所有大于等于Manifest里记录的Lognumer和等于prevLognum的Wal日志恢复Memtable。元数据管理ManifestManifest中存储了leveldb的元数据当前使用的Manifest文件名存储在Current文件中。比如在前面的例子中testdb目录下的文件有000003.log CURRENT LOCK LOG MANIFEST-000002其中CURRENT中的内容是MANIFEST-000002。Manifest的存储内容和格式写入文件是是按照Log的存储格式写入的。读上来的record在更具Manifest的格式进行解析其格式如下header是Log日志格式中的header包含如下内容checksum:uint32// crc32c of type and data[] ; little-endianlength:uint16// little-endiantype:uint8// One of FULL, FIRST, MIDDLE, LASTtag是Manifest编码解码使用的tag包含的值有enumTag{kComparator1,// 记录当前使用的比较器名称如 leveldb.BytewiseComparator。kLogNumber2,// 当前写前日志WAL的文件编号kNextFileNumber3,// 下一个要分配的文件编号kLastSequence4,// 最后一次写入的序列号kCompactPointer5,// 记录某个 level 上次 compaction 进行到的key 位置。下次 compaction 从此处继续kDeletedFile6,// 标记某个 SSTable 文件已删除kNewFile7,// 新增一个 SSTable 文件到某个 level// 8 was used for large value refs已废弃kPrevLogNumber9// 上一个 WAL 日志编号};length 为后面value的长度value即为对应tag的取值比如kComparator对应的就是比较器的名字Manifest的代码实现Manifest的实现有以下几个核心的类VersionVersionEditVersionSet。以修改Manifest为例子修改先写入VersionEdit然后应用于Version生成新的Version挂载到VersionSetVersionSet负责将VersionEdit中的修改内容持久化到Manifest文件中。Manifest的修改时机主要是在compaction完成之后具体的实现在LogAndApply中。Manifest文件只有在Open的时候可能会创建新的那如果一直运行不停文件会一直膨胀Version代表 LevelDB 在某一个特定时间点的全局文件状态快照。它记录了当前所有 Level 分别包含哪些 SSTable 文件。Version是一个双向链表每一个几点都是一个快照。其定义如下// db/version_set.hclassVersion{private:VersionSet*vset_;// VersionSet to which this Version belongsVersion*next_;// Next version in linked listVersion*prev_;// Previous version in linked listintrefs_;// Number of live refs to this version// List of files per levelstd::vectorFileMetaData*files_[config::kNumLevels];// Next file to compact based on seek stats.FileMetaData*file_to_compact_;intfile_to_compact_level_;// Level that should be compacted next and its compaction score.// Score 1 means compaction is not strictly needed. These fields// are initialized by Finalize().doublecompaction_score_;intcompaction_level_;};VersionEdit代表一次状态变更的增量。它不记录全量状态只记录“变化了什么”。主要内容新增加了哪些 SST 文件在哪个 Level。删除了哪些 SST 文件从哪个 Level。日志编号、下一个文件号等元信息的变化。// db/version_edit.hclassVersionEdit{typedefstd::setstd::pairint,uint64_tDeletedFileSet;std::string comparator_;uint64_tlog_number_;uint64_tprev_log_number_;uint64_tnext_file_number_;SequenceNumber last_sequence_;boolhas_comparator_;boolhas_log_number_;boolhas_prev_log_number_;boolhas_next_file_number_;boolhas_last_sequence_;std::vectorstd::pairint,InternalKeycompact_pointers_;DeletedFileSet deleted_files_;std::vectorstd::pairint,FileMetaDatanew_files_;};VersionSet元数据的管理者主要作用维护一个双向链表管理所有的VersionVersionEdit序列化并写入到 MANIFEST文件中。其成员变量如下// db/version_set.hclassVersionSet{Env*constenv_;conststd::string dbname_;constOptions*constoptions_;TableCache*consttable_cache_;constInternalKeyComparator icmp_;uint64_tnext_file_number_;uint64_tmanifest_file_number_;uint64_tlast_sequence_;uint64_tlog_number_;uint64_tprev_log_number_;// 0 or backing store for memtable being compacted// Opened lazilyWritableFile*descriptor_file_;log::Writer*descriptor_log_;Version dummy_versions_;// Head of circular doubly-linked list of versions.Version*current_;// dummy_versions_.prev_// Per-level key at which the next compaction at that level should start.// Either an empty string, or a valid InternalKey.std::string compact_pointer_[config::kNumLevels];};