leveldb插入数据时,必然做的操作是先写logfile,再将数据放到cache里
不过在此之前,会先进行一下预处理
1 将要写的数据封装到到writer里,将write加入写队列,等待轮到它写。
2 检查cache是否已满,是否需要“做检查点”
3 leveldb的cache有两个状态,当前状态和只读状态。
当cache写满,需要写文件时,会将cache转成只读状态,进行写文件和文件压缩操作。
所以每次写文件前都要先等待之前的只读cache完成自己的使命。
4 由于新数据需要些到level 0文件,而level 0文件的个数是有限制的
当达到soft limit时,需要sleep1毫秒,将cpu资源让给正在进行中的压缩操作。
当达到hard limits时,直接进入等待。
5 维护cache状态,维护file number,创建新文件。。。
6 尝试进行一次文件压缩。
对外的put函数
-
Status DBImpl::Put(const WriteOptions& o, const Slice& key, const Slice& val) {
-
return DB::Put(o, key, val);
-
}
-
-
-
Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) {
-
WriteBatch batch;
-
batch.Put(key, value);
-
return Write(opt, &batch);
-
}
真正的功能入口函数
之所以需要MakeRoom是因为新数据需要写入level 0 数据文件,但是level 0文件数量有限制。
可能需要做压缩来减少level 0 文件的数量。
同时当前cache也需要转成imm cache,需要判断之前的imm cache是否还占着位置。
-
Status DBImpl::MakeRoomForWrite(bool force) {
-
mutex_.AssertHeld();
-
assert(!writers_.empty());
-
// 决定是否允许通过sleep来给压缩操作让出系统资源。
-
bool allow_delay = !force;
-
Status s;
-
while (true) {
-
if (!bg_error_.ok()) {
-
// Yield previous error
-
s = bg_error_;
-
break;
-
} else if (
-
/*
-
当允许delay,并且level 0的文件数已经超过了8个,就要sleep 1毫秒,给复制压缩的线程工作让出CPU资源。
-
sleep一次后就将allow_delay设成false,这次写入操作就不需要再sleep了。
-
*/
-
allow_delay &&
-
versions_->NumLevelFiles(0) >= config::kL0_SlowdownWritesTrigger) {
-
// We are getting close to hitting a hard limit on the number of
-
// L0 files. Rather than delaying a single write by several
-
// seconds when we hit the hard limit, start delaying each
-
// individual write by 1ms to reduce latency variance. Also,
-
// this delay hands over some CPU to the compaction thread in
-
// case it is sharing the same core as the writer.
-
mutex_.Unlock();
-
env_->SleepForMicroseconds(1000);
-
allow_delay = false; // Do not delay a single write more than once
-
mutex_.Lock();
-
} else if (!force &&
-
/*
-
当cache不满时,先不写文件。
-
*/
-
(mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)) {
-
// There is room in current memtable
-
break;
-
} else if (imm_ != NULL) {
-
/*
-
leveldb有两种cache,一个是当前cache,就是目前正在写新数据的cache。
-
当cache满了,需要写文件时,就将当前cache转成immunity cache,是一个只读cache,由指针imm_管理。
-
imm cache 用户查询操作和压缩操作。
-
如果imm cache存在,就要等它的对应的文件压缩完成才能将当前cache转成imm cache。
-
*/
-
// We have filled up the current memtable, but the previous
-
// one is still being compacted, so we wait.
-
Log(options_.info_log, "Current memtable full; waiting...\n");
-
bg_cv_.Wait();
-
} else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) {
-
// 达到了level 0 文件数的硬指标限制,不能再写新的了。
-
// There are too many level-0 files.
-
Log(options_.info_log, "Too many L0 files; waiting...\n");
-
bg_cv_.Wait();
-
} else {
-
// 检查条件结束,开始正式工作
-
// Attempt to switch to a new memtable and trigger compaction of old
-
assert(versions_->PrevLogNumber() == 0);
-
-
-
// 生成新的logfile number
-
uint64_t new_log_number = versions_->NewFileNumber();
-
WritableFile* lfile = NULL;
-
// 创建新文件
-
s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile);
-
if (!s.ok()) {
-
// Avoid chewing through file number space in a tight loop.
-
versions_->ReuseFileNumber(new_log_number);
-
break;
-
}
-
delete log_;
-
delete logfile_;
-
-
-
// 将Logfile指向新文件,设置新log number
-
logfile_ = lfile;
-
logfile_number_ = new_log_number;
-
log_ = new log::Writer(lfile);
-
-
-
// 将当前cache切换成imm cache,创建新的当前cache
-
imm_ = mem_;
-
has_imm_.Release_Store(imm_);
-
mem_ = new MemTable(internal_comparator_);
-
mem_->Ref();
-
force = false; // Do not force another compaction if have room
-
-
-
/*
-
如果需要,进行一次压缩
-
这里面进行了一下判断,调了回调函数
-
最终真正的功能入口是DBImpl::BackgroundCompaction()
-
*/
-
MaybeScheduleCompaction();
-
}
-
}
-
return s;
-
}
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/26239116/viewspace-1847246/
,如需转载,请注明出处,否则将追究法律责任。