LevelDB源码解析3——get
LevelDB的get操作
leveldb源码分析3
前面的Put
操作实际上包括了写入和删除,接下来分析Get
操作。
总的来说,Get
操作就是找到userkey
相同,并且SequenceNumber
最大(最新)的数据。
DBImpl::Get(db/db_impl.cc
1118行)
Status DBImpl::Get(const ReadOptions& options,
const Slice& key,
std::string* value) {
Status s;
MutexLock l(&mutex_);
SequenceNumber snapshot;
if (options.snapshot != nullptr) {
snapshot =
static_cast<const SnapshotImpl*>(options.snapshot)->sequence_number();
} else {
snapshot = versions_->LastSequence();
}
leveldb支持对特定Snapshot
的Get
,只是简单的将Snapshot
的SequenceNumber
作为最大的SequenceNumber
即可。
继续Get
函数,1131~1155行:
MemTable* mem = mem_;
MemTable* imm = imm_;
Version* current = versions_->current();
mem->Ref();
if (imm != nullptr) imm->Ref();
current->Ref();
bool have_stat_update = false;
Version::GetStats stats;
// Unlock while reading from files and memtables
{
mutex_.Unlock();
// First look in the memtable, then in the immutable memtable (if any).
LookupKey lkey(key, snapshot);
if (mem->Get(lkey, value, &s)) { // 在memtable中查找
// Done
} else if (imm != nullptr && imm->Get(lkey, value, &s)) { // 在memtable中未找到,则在immutable memtable中查找
// Done
} else { // 仍未找到,在SSTable中查找
s = current->Get(options, lkey, value, &stats);
have_stat_update = true;
}
mutex_.Lock();
}
由于写leveldb的时候还没出C++11标准,所以memtable使用自己写的引用计数,而没有用智能指针。
查找的顺序是从memtable
开始找,然后到immutable memtable
,最后才在SSTable
中查找,
从level-0开始,每个level上依次进行查找。
在由于memtable
和immutable memtable
底层都是skip list
实现,所以MemTable::Get()
都是
通过skip list
来查找。
Version::Get(db/version_set.cc
332行)
由于代码过长,所以就简单写一写流程:
- 首先找出level上可能包含key的
sstable
.(key包含在FileMetaData(FileMetaData
是SSTable
文件的元信息的封装)的[startest,largest].level-0
的查找只能顺序遍历files_[0]
。考虑到level-0
中的sstable
是memtable
dump生成的,所以新生成的sstable
一定比旧生成有更新的数据,同时sstable
文件的FileNumber
是递增,所以,将从 level-0 中获得的sstable(FileMetaData)
按照FileNumber
排序( NewestFirst()db/version_set.cc
),能够优化level-0
中的查找。level-0
中可能会找到多个sstable
- 非
level-0
中的查找,对files_[]
基于FileMetaData::largest
做二分查找 (FindFile()db/version_set.cc
)即可定位到level中可能包含key
的sstable
。非level-0
上最多找到一个 sstable。
- 如果该level上没有找到可能的
sstable
,跳过。否则,对要进行查找的 sstable 获得其Iterator(TableCache::NewIterator()
),做seek()
. - seek 成功则检查有效性( GetValue()
db/version_set.cc
)也就是根据ValueType
判断是否是有效的数据:kTypeValue
,返回对应的value
数据。kTypeDeletion
,返回data not exist
总结
从之前介绍的LevelDb的写操作和这里介绍的读操作可以看出,相对写操作,读操作处理起来要复杂很多, 所以写的速度必然要远远高于读数据的速度,也就是说,LevelDb比较适合写操作多于读操作的应用场合。 而如果应用是很多读操作类型的,那么顺序读取效率会比较高,因为这样大部分内容都会在缓存中找到,尽可能避免大量的随机读取操作。