PUT
LevelDB的写入性能较高,整体步骤就是先写wal日志再写入memtable中,由于wal日志是append写入的,性能较高,所以写入一般不存在瓶颈。但是如果写入速度过快,导致memtable来不及flush或者lelvel 0中的文件数较多时,系统的写入可能会被限制,这就是LevelDB的write stall问题,整体写入流程如下:
1 | struct DBImpl::Writer { |
GET
LevelDB支持获取,某个key的指定版本对应的值,如果没有指定版本的话,则获取最新的kv对,整体流程如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53Status DBImpl::Get(const ReadOptions& options,
const Slice& key,
std::string* value) {
Status s;
//获取快照和version时先加锁,保证相关引用的正确修改
MutexLock l(&mutex_);
SequenceNumber snapshot;
//获取相关sequenceNUmber
if (options.snapshot != NULL) {
snapshot = reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_;
} else {
snapshot = versions_->LastSequence();
}
MemTable* mem = mem_;
MemTable* imm = imm_;
Version* current = versions_->current();
//memtable引用加1
mem->Ref();
if (imm != NULL) imm->Ref();
//version引用加1
current->Ref();
bool have_stat_update = false;
Version::GetStats stats;
//ref已经设置完成,可以释放锁了
{
mutex_.Unlock();
// 在memtable中查找
LookupKey lkey(key, snapshot);
if (mem->Get(lkey, value, &s)) {
// 在imm中查找
} else if (imm != NULL && imm->Get(lkey, value, &s)) {
// Done
} else {
//通过version查找,一个version代表DB中sstable文件的一个状态
s = current->Get(options, lkey, value, &stats);
have_stat_update = true;
}
//确保只有一个线程修改引用
mutex_.Lock();
}
if (have_stat_update && current->UpdateStats(stats)) {
//如果扫描次数过多可能会触发compaction
MaybeScheduleCompaction();
}
mem->Unref();
if (imm != NULL) imm->Unref();
current->Unref();
return s;
}
其中要注意的是,要通过锁来确保只有一个线程来修改相关的引用计数,修改完引用后可以释放锁,即读取memtable和sstable不是互斥的。