写点什么

Leveldb 解读之三:Write

用户头像
Jowin
关注
发布于: 2021 年 04 月 28 日

这一篇我们来分析 leveldb 的 write,写流程相对比较简单。

Write 流程


流程说明:


  • 使用 WriteBatch 封装要写入的多个 kv,然后调用 push 到 writebatch 队列。

  • 多个写线程在 writebatch 队列上等待,等到任务执行完毕(被其他 writer 捎带执行),或者任务排到了队列头部,可以开始执行。

  • 写过程首先调用 MakeRoomForWrite 准备 Memtable 表,然后从 writebatch 队列中出队一批写任务,打包一起执行。

  • 执行过程很简单,标准 WAL 写法,先写 log,再写 memtable,适当的时候把 memtable 刷盘。

MakeRoomForWrite
  • 如果当前 Memtable < write_buffer_size(缺省是 4M),则什么都不做,继续使用当前 Memtable。

  • 否则,关闭当前 Memtable(改为 imm)和 log 文件,切换到新的 Memtable 和 log 文件。


有一个细节,MakeRoomForWrite可能会引入延时:  (1) 当level-0文件数量 > kL0_SlowdownWritesTrigger(8个)时,会主动等待1ms,再次检查;  (2) 当level-0文件数量 > kL0_StopWritesTrigger(12个)时,会等待后台压缩线程结束,再次检查;
复制代码
写操作的原子性

写操作使用 WriteBatch 封装一批要写入的 kv,然后打包成一条记录写入 log 文件,MemTable 是逐个 key 插入的,但只是中间状态(不会失败,但是对其他 Reader 可见,不满足隔离性)。因此,使用 WriteBatch 写入/删除多个 key 是原子操作。

持久性

leveldb 支持同步写,设置 wirte_option.sync,写操作在调用 log->append()操作后,会调用 logfile->Sync(),等待操作系统把文件缓存刷入硬盘,但同步写性能比较差。


在 leveldb 的官方文档里面也提到了,一般情况下,异步写完全够用,


Note that a crash of just the writing process (i.e., not a reboot) will not cause any loss since even whensync is false, an update is pushed from the process memory into the operating system before it is considereddone.
复制代码
Memtable

Memtable 使用的 skiplist 是线程安全的,支持一读多写并发,这对读写并发度高的业务非常友好。

Sequence

在 leveldb 中使用 sequence 序号来实现 key 值版本管理和 snapshot。在 Memtable 中,key 的格式是:


{user_key,sequence(最低8位是Type,ValueType or DeleteType)},每个key对应一个唯一序号。
复制代码


在写入 sstable file 时,也是按这个格式编码,但会对 user_key 进行前缀压缩。

Options 参数
(1) options.max_file_size缺省是2M,但由于Memtable的缺省大小是4M,因此Memtable刷入sstable时文件大小可能会超过2M,这是很正常的。在compaction时,会重新分割文件,使之符合2M的大小约束。
再考虑一个特殊情况,假如一个key的value>2M,即便把它单独放到一个文件中,它的大小也会>2M。
(2) options.block_size缺省是4K,这也是一个近视参考值,不是严格约束。
复制代码


用户头像

Jowin

关注

爱代码,爱生活 2013.08.19 加入

C++/Go Programmer,关注分布式存储,目前从事金融数据开发。

评论

发布
暂无评论
Leveldb解读之三:Write