LevelDB源码解析2——put
LevelDB的put操作
leveldb源码分析2
在了解了Open
操作后,接下来分析Put
操作。
DB::Put(db/db_impl.cc
1487行)
// Default implementations of convenience methods that subclasses of DB
// can call if they wish
Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) {
WriteBatch batch;
batch.Put(key, value);
return Write(opt, &batch);
}
WriteBatch
(db/write_batch.cc):对若干数目的key的write操作(put/delete)封装成WriteBatch
。它会将userkey
连同SequenceNumber
(db/dbformat.h,leveldb中的每次更新put/delete操作都拥有一个版本,由SequenceNumber
标识,真个db有一个全局值保存着当前用到的SequenceNumber
)和ValueType
先做encode,然后做decode,将数据insert到指定的Handler(memtable)上面。
WriteBatch::Put
接着上面的batch.Put(key, value);
,跳转到write_batch.cc
102行:
void WriteBatch::Put(const Slice& key, const Slice& value) {
WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1);
rep_.push_back(static_cast<char>(kTypeValue));
PutLengthPrefixedSlice(&rep_, key);
PutLengthPrefixedSlice(&rep_, value);
}
这个函数主要就是将key-value封装WriteBatch
DBImpl::Write(db/db_impl.cc
1204行)
由于代码太长就不贴上来了,简单描述一下流程:
1. 循环检查当前 db 状态,确定策略(DBImpl:: MakeRoomForWrite()
):
+ 如果当前level-0
中的文件数目达到kL0_SlowdownWritesTrigger
阈值,则sleep进行
delay。该delay只会发生一次。
+ 如果当前memtable
的size
未达到阈值write_buffer_size
,则允许这次写。
+ 如果memtable
已经达到阈值,但immutable memtable
仍存在,则等待compact
将其dump完成。
+ 如果 level-0 中的文件数目达到 kL0_StopWritesTrigger 阈值,则等待compact memtable
完成
+ 上述条件都不满足,则是 memtable 已经写满,并且 immutable memtable 不存在,则将当前
memtable 置为 immutable memtable,生成新的 memtable 和 log file,主动触发 compact,允许该次写。
2. 设置WriteBatch
的SequnceNumber
。
3. 先将WriteBatch
中的数据记 log(Log::AddRecord()
)。
4. 将WriteBatch
应用在memtable
上。(WriteBatchInternal::InsertInto()
) ,即遍历decode出
WriteBatch
中的key/value/ValueType
,根据ValueType
对memetable
进行put/delete操作。
5. 更新SequnceNumber(
last_sequnce + WriteBatch::count()`)。
总结
完成Put
操作包括两个步骤:首先是将这条KV记录以顺序写的方式追加到log文件末尾,尽管是一个磁盘读写操作,
但是文件的顺序追加写入效是很高的,因此不会导致写入速度降低;第二个步骤是如果写入log文件成功,那么将这条KV记录
插入内存的memtable
中。