Write
还是跟着leveldb handbook去看代码。这次主要是读写操作
主要是走马观花的看一下写操作的实现。
写操作对应的接口主要是Put和Delete
他们都对应了batch中的一个数据
Put会包含Key和Value,而Delete则只含有Key
他们都会调用Write来进行写入
leveldb同一时刻只允许一个写入操作将内容写入到日志以及数据库中。为了减少日志文件的写入,同时增加整体的写入性能。leveldb会将若干个小写入合并为一个大写入。
将当前的Write追加到writers中。然后等待。这里的条件会判断,如果其他人合并了当前的写,他们会设置当前写的done为true,然后signal,这样当前的写入操作就知道自己已经写完了。或者当前操作成为了队列中的第一个writer,则他获得写入权限。
记录上次的sequence number,为本次写入申请空间,这个操作可能会导致compaction
这里build batch group会从last writer开始遍历writers_
,直到没有其他writer,或者是本次batch空间满了。这个操作就是合并写
然后为本次batch增加写操作数量的sequence count
释放锁,因为写入log是一个耗时的操作,我们希望这时候其他的writer可以继续append request
append log失败的话就退出。成功的话我们就调用InsertInto。等下会回来继续看这个函数。因为他会执行插入到memtable的操作
本次write batch写入成功后。要唤醒本次write batch的其他writer,告诉他们写入结果。
遍历的时候会跳过当前的writer,因为他没有在cv上等待。直到遍历到write batch的最后一个writer,这个值在BuildBatchGroup内遍历的时候会设置,就会break。
最后如果发现还有其他request的话,就唤醒第一个writer,让他获得写权限并进行下一批的写入。
核心的耗时操作主要在Append Log中,以及memtable的插入。这两个操作都是在临界区外进行的。mutex的主要作用是保护writer_
,保证write request一个一个插入。
leveldb中的memtable是append only的,核心思路和LSM整体思路是相同的,通过版本来确定值的最新状态。所以无论是Put还是Delete都对应了memtable的Add,只不过type不同而已。
b->Iterate
会遍历write batch中的每个操作,并根据他的类型去调用handler中对应的操作
这里通过Slice操作将key和value拿出来不会涉及到太多的开销。因为他不会涉及到额外的拷贝,只会反序列化一个长度。
这里之所以用一个额外的序列化和反序列化,而不是将操作存在内存结构中从而快速取出KV是因为我们的write batch和log的相关性比较强。并且我们将数据都存在了WriteBatch中,我们可以通过额外的内存结构中的Slice来记录每个操作的key和value,但是这个开销相比与从write batch中重建kv开销更大。因为引入了额外的空间,并且计算开销仍然存在。
memtable中的add会将kv序列化到一起。存到buf中。格式就如同注释中的一样,根据key size, key, tag, val size, val的形式序列化,其中tag前7字节为sequence number,最后一个字节为type
最后调用insert插入到skiplist中。之后我们会针对性的分析skip list。
文章评论