LevelDB源码解析2——put
LevelDB的put操作
leveldb源码分析2
在了解了Open操作后,接下来分析Put操作。
DB::Put(db/db_impl.cc1487行)
// 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.cc102行:
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.cc1204行)
由于代码太长就不贴上来了,简单描述一下流程:
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中。