上面的log文件,sst文件,临时文件,清单文件末尾都带着序列号,序号是单调递增的(随着next_file_number从1开始递增),以保证不会和之前的文件名重复。另外,注意区分db log与info log:前者是为了防止保障数据安全而实现的二进制Log,后者是打印引擎中间运行状态及警告等信息的文本log。
随着更新与Compaction的进行,LevelDB会不断生成新文件,有时还会删除老文件,所以需要一个文件来记录文件列表,这个列表就是清单文件(manifest文件)的作用,清单会不断变化,DB需要知道最新的清单文件,必须将清单准备好后原子切换,这就是CURRENT文件的作用,LevelDB的清单过程更新如下:
1. 递增清单序号,生成一个新的清单文件。
2. 将此清单文件的名称写入到一个临时文件中。
3. 将临时文件rename为CURRENT。
LevelDB无论是创建数据库,还是打开现有的数据库,都是使用Open方法。代码如下:
Status DB::Open(const Options& options, const std::string& dbname,
DB** dbptr) {
*dbptr = NULL;
DBImpl* impl = new DBImpl(options, dbname);
impl->mutex_.Lock();
VersionEdit edit;
bool save_manifest = false;
// Recover逻辑,如果存在数据库,则Load数据库数据,并对日志进行恢复,否则,创建新的数据库
Status s = impl->Recover(&edit, &save_manifest); //具体可看recover那篇博文
if (s.ok() && impl->mem_ == NULL) {
//impl->mem_ == NULL说明没有继续使用旧日志需创建新的日志
// 从version_set获取一个新的文件序号用于日志文件,所以如果是新建的数据库,则第一个LOG的
//序号为2(1已经被MANIFEST占用,NewDB代码里可以看出)
uint64_t new_log_number = impl->versions_->NewFileNumber();
// 记录日志文件号,创建新的log文件及Writer对象
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_ = new log::Writer(lfile);
impl->mem_ = new MemTable(impl->internal_comparator_);
impl->mem_->Ref();
}
}
if (s.ok()&& save_manifest) {
//save_manifest说明有新的sst文件生成,需要记录在manifest文件
edit.SetPrevLogNumber(0); // No older logs needed after recovery.
edit.SetLogNumber(impl->logfile_number_);
//产生了新log,就log已经回放完毕了,应用edit生成新版本
s = impl->versions_->LogAndApply(&edit, &impl->mutex_);
}
if(s.ok())
{
// 删除废弃的文件(如果存在)
impl->DeleteObsoleteFiles();
// 检查是否需要Compaction,如果需要,则让后台启动Compaction线程
impl->MaybeScheduleCompaction();
}
impl->mutex_.Unlock();
if (s.ok()) {
*dbptr = impl;
} else {
delete impl;
}
return s;
}
创建一个新数据库也需要DB的Open方法
创建一个新数据库的调用流程如下:
DB::Open
DBImpl* impl = new DBImpl
impl->Recover //详见leveldb:数据库recover机制
DBImpl::NewDB
//DBImpl在构造时会初始化互斥体与信号量,创建一个空的memtable,并根据配置设置
//Comparator及LRU缓冲
DBImpl::DBImpl(const Options& raw_options, const std::string& dbname)
: env_(raw_options.env),
internal_comparator_(raw_options.comparator),// 初始化Comparator
internal_filter_policy_(raw_options.filter_policy),
// 检查参数是否合法
options_(SanitizeOptions(dbname, &internal_comparator_,
&internal_filter_policy_, raw_options)),
// 是拥有自己info log,还是使用用户提供的
owns_info_log_(options_.info_log != raw_options.info_log),
// 是否拥有自己的block LRU cache,或者使用用户提供的
owns_cache_(options_.block_cache != raw_options.block_cache),
dbname_(dbname),// 数据库名称
db_lock_(NULL),
shutting_down_(NULL),
bg_cv_(&mutex_),// 用于与后台线程交互的条件信号
mem_(NULL),// 跳表初识为NULL
imm_(NULL),
logfile_(NULL),
logfile_number_(0),// log文件的序号
log_(NULL),
seed_(0),
tmp_batch_(new WriteBatch),//用于write
bg_compaction_scheduled_(false),// 当前是否有后台的compaction线程正在进行合并
manual_compaction_(NULL) {
has_imm_.Release_Store(NULL);
// 设置table LRU cache的Entry数目不能超过max_open_files-10
const int table_cache_size = options_.max_open_files - kNumNonTableCacheFiles;
//table_cache_是各个sst文件元数据(index块以及布隆块)的缓存
table_cache_ = new TableCache(dbname_, &options_, table_cache_size);
//leveldb一共使用了两种lru cache,它们的功能不同,一个是table_cache,一个是block_cache
// 创建一个Version管理器
versions_ = new VersionSet(dbname_, &options_, table_cache_,
&internal_comparator_);
}
Options SanitizeOptions(const std::string& dbname,
const InternalKeyComparator* icmp,
const Options& src) {
Options result = src;
result.comparator = icmp;
ClipToRange(&result.max_open_files, 20, 50000);
ClipToRange(&result.write_buffer_size, 64<<10, 1<<30);
ClipToRange(&result.block_size, 1<<10, 4<<20);
// 如果用户未指定info log文件(用于打印状态等文本信息的日志文件),则由引擎自己创建一个info log文件。
if (result.info_log == NULL) {
// Open a log file in the same directory as the db
src.env->CreateDir(dbname); // 如果目录不存在则创建
// 如果已存在以前的info log文件,则将其改名为LOG.old,然后创建新的log文件与日志的writer
src.env->RenameFile(InfoLogFileName(dbname), OldInfoLogFileName(dbname));
Status s = src.env->NewLogger(InfoLogFileName(dbname), &result.info_log);
if (!s.ok()) {
result.info_log = NULL;
}
}
// 如果用户没指定block_cache LRU缓冲,则创建8MB的LRU缓冲
if (result.block_cache == NULL) {
result.block_cache = NewLRUCache(8 << 20);
}
return result;
}
Status DBImpl::NewDB() {
// 创建version管理器
VersionEdit new_db;
// 设置Comparator
new_db.SetComparatorName(user_comparator()->Name());
new_db.SetLogNumber(0);
// 下一个序号从2开始,1留给清单文件
new_db.SetNextFile(2);
new_db.SetLastSequence(0);
// 创建一个清单文件,MANIFEST-1
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()) {
// 设置CURRENT文件,使其指向清单文件
s = SetCurrentFile(env_, dbname_, 1);
} else {
env_->DeleteFile(manifest);
}
return s;
}