前面已经写了几篇文章介绍一些和 LevelDB 相关的内容:
这篇文章,介绍一下 LevelDB 的写操作。
LevelDB 提供的写操作接口有:
其中,Put 和 Delete 的实现都是通过封装 Write 来实现的,函数调用关系如下:
Write 的函数原型如下:
virtual Status Write(const WriteOptions& options, WriteBatch* updates) = 0;
leveldb::WriteOptions 是控制写操作的参数,目前只有一个成员变量 sync
表示是否每次写完都要将日志 flush 到磁盘。
leveldb::WriteBatch 表示多个 Key-Value 数据的更新操作(Put、Delete)。
具体实现是 leveldb::DBImpl::Write 。
struct DBImpl::Writer {
Status status; // 执行结果
WriteBatch* batch; // 多个更新操作
bool sync; // 是否 flush 到磁盘,WriteOptions.sync
bool done; // 是否已经执行
port::CondVar cv; // 并发控制的条件变量
explicit Writer(port::Mutex* mu) : cv(mu) { }
};
MakeRoomForWrite.png
1. 合并写入的数据大小,[默认 max\_size 是 1MB (1 << 20)](https://link.jianshu.com/?t=https://github.com/google/leveldb/blob/v1.20/db/db_impl.cc#L1280)。如果第一个写请求的 size 比较小(小于128 KB, 128 << 10),则 [max\_size 为 size + 128 KB](https://link.jianshu.com/?t=https://github.com/google/leveldb/blob/v1.20/db/db_impl.cc#L1282)。这样做是为了避免数据小的请求被其它请求给拖慢。
2. 如果第一个写请求 sync == false,那么就不要加入 sync == true 的写请求。
以上,便是 LevelDB 的写入流程。
本篇文章结合代码简单介绍了 LevelDB 写操作的流程,其中,写入队列 + 合并写操作 是 LevelDB 写操作的一个设计亮点 —— 至少我个人觉得这个设计简单又实用,对写入性能的提升也应该是立竿见影的。
当然,LevelDB 的写操作也存在一些可以改进的地方,比如整个写入过程——包括写日志和写 MemTable,都是单线程的。