前3篇文章实际上都是在介绍LevelDB辅助代码,无论是跳表,LRU Cache还是布隆过滤器,这篇文章主要希望一起来探寻下LevelDB向数据库中写入时,涉及的主要代码流程是什么。在继续下面的内容之前,读者最好先对LevelDB的核心概念和设计思想有些基本的知识,这里推荐几篇文章供参考:
01| LevelDB架构分析
LevelDB 的原理
写入的总体流程
Write --> Log file(WAL日志) --> Memtable
(后台调度) --> Memtable --> immutable Memtable --> SST file
(后台调度) --> SST file@level L --> SST file @level L+1
保存Key的入口
从include/leveldb/db.h中可以看到,LevelDB对外暴露的接口非常简洁,最常用的应该是Open/Put/Write(批量Put)/Delete/Get,其他的包括遍历时用的NewIterator,获取当前DB快照(Snapshot)和统计信息等,至于手动Compact等接口相对高阶,一般不太使用。
当我们想要保存单个Key时,打开或创建DB后,可直接调用下面的接口
virtual Status Put(const WriteOptions& options, const Slice& key,
const Slice& value) = 0;
关于Slice类型简单插一句,它是字符串切片(有Golang或者Python等编程经验的朋友可以直接做类比)。切片是字符串上一种引用,它并不保存实际的string数据,只关注数据的起始位置和长度,如下
Class Slice {
private:
const char* data_;
size_t size_;
};
回到正题,调用这个Put接口实际上到达了DBImpl::Write(const WriteOptions& options, WriteBatch* updates)函数,也就是说写单个Key的Put接口,实际是Write接口(批量写)的Convenient function而已。接下来我们看一下Write函数的内部做了哪些事。
保存Key-Value pair到Memtable
函数先是创建了一个Writer并放入Writer队列(FIFO)std::deque<Writer*> writers_ GUARDED_BY(mutex_);中,并一直等待轮到自己后再继续。这里用到的技巧是使用条件变量(conditinal variable)来唤醒队列中的任务,这是个比较常用的技巧,比如可用来实现线程池,更详细的内容读者朋友可以搜索下相关资料。
之后检查一下MemTable,MakeRoomForWrite(updates == nullptr);,确保有Memtable中有足够的剩余空间容纳当前的写操作(mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)),这里write_buffer_size是Memtable的大小(可配置,在OpenDataBase的时候可以指定)。如果空间不足,该函数会创建一个新的Memtable,并把旧Memtable(DBImpl::mem_)放入immutable Memtable(DBImpl::imm_),并在后台尝试进行Compaction(后面会继续讨论)。把KV pair写入MemTable前,先写log(WAL,write-ahead log)文件,并根据options.sync调用Sync接口将log落盘。在类Posix系统上,一般是通过系统调用fdatasync或者fsync。
mutex_.Unlock();
status = log_->AddRecord(WriteBatchInternal::Contents(write_batch));
bool sync_error = false;
if (status.ok() && options.sync) {
status = logfile_->Sync();
if (!status.ok()) {
sync_error = true;
}
}
if (status.ok()) {
status = WriteBatchInternal::InsertInto(write_batch, mem_);
}
mutex_.Lock();
WAL日志在数据库系统中使用比较广泛,可(在数据库异常崩溃时)找回未写入硬盘的修改,减少数据丢失。MemTable在内存中,其数据结构类型正是之前介绍过的跳表,因为LevelDB把数据写入Memtable后就认为成功,所以并发写入性能相对较好。

浙公网安备 33010602011771号