LevelDB:写操作

前面已经写了几篇文章介绍一些和 LevelDB 相关的内容:

这篇文章,介绍一下 LevelDB 的写操作。

写操作接口

LevelDB 提供的写操作接口有:

  • Put :更新(增/改)一条记录。
  • Delete :删除一条记录。
  • Write :原子地更新(增/删/改)多条记录。

其中,Put 和 Delete 的实现都是通过封装 Write 来实现的,函数调用关系如下:

Write

接口

Write 的函数原型如下:

virtual Status Write(const WriteOptions& options, WriteBatch* updates) = 0;

leveldb::WriteOptions 是控制写操作的参数,目前只有一个成员变量 sync 表示是否每次写完都要将日志 flush 到磁盘。

leveldb::WriteBatch 表示多个 Key-Value 数据的更新操作(Put、Delete)。

具体实现是 leveldb::DBImpl::Write

实现

  1. LevelDB 通过传入的参数构造一个 Writer —— leveldb::DBImpl::Writer 对象来执行本次批量写操作:
struct DBImpl::Writer {
  Status status;            // 执行结果
  WriteBatch* batch;        // 多个更新操作
  bool sync;                // 是否 flush 到磁盘,WriteOptions.sync
  bool done;                // 是否已经执行
  port::CondVar cv;         // 并发控制的条件变量

  explicit Writer(port::Mutex* mu) : cv(mu) { }
};
  1. 获取互斥锁将自己放入写队列等待别人帮忙完成写入或者成为队首获得执行写入的权限。这里涉及 LevelDB 写操作的一个性能优化:执行写入操作的线程,会根据一定的规则将队列中的多个请求合并成一个请求,然后执行批量写入,并更新各个 Writer 的状态。
  2. 如果别人帮忙完成写入了,直接返回结果。下面开始执行写入数据。
  3. 调用 MakeRoomForWrite 在一个循环里面按照下面的流程进行检查,直到MemTable 的大小没有达到阈值或者出错。(MakeRoomForWrite 提供一个 force 参数表示是否强制切换新 MemTable,并触发 Compaction。正常写流程 force 为 false。)

MakeRoomForWrite.png

  1. 调用 BuildBatchGroup 将从队首开始的连续多个符合条件的 Writer 的写请求合并到 tmp_batch_。合并时主要考虑:
    1. 合并写入的数据大小,默认 max_size 是 1MB (1 << 20)。如果第一个写请求的 size 比较小(小于128 KB, 128 << 10),则 max_size 为 size + 128 KB。这样做是为了避免数据小的请求被其它请求给拖慢。
    2. 如果第一个写请求 sync == false,那么就不要加入 sync == true 的写请求。
  2. 设置写入数据的 sequence
  3. 释放互斥锁。这里代码保证同一时刻只有一个线程会执行写入操作。
  4. 写日志(WAL)
  5. 根据参数决定是否 sync 日志
  6. 更新 MemTable
  7. 获取互斥锁
  8. 如果 sync 失败设置 bg_error_,后续所有写入都将失败。
  9. 清空临时合并的批量操作
  10. 更新 LastSequence
  11. 通知所有数据已经被写入的线程
  12. 通知写队列剩余的线程

以上,便是 LevelDB 的写入流程。

小结

本篇文章结合代码简单介绍了 LevelDB 写操作的流程,其中,写入队列 + 合并写操作 是 LevelDB 写操作的一个设计亮点 —— 至少我个人觉得这个设计简单又实用,对写入性能的提升也应该是立竿见影的。

当然,LevelDB 的写操作也存在一些可以改进的地方,比如整个写入过程——包括写日志和写 MemTable,都是单线程的。

参考文档

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏玄魂工作室

Python黑客编程3网络数据监听和过滤

课程的实验环境如下: • 操作系统:kali Linux 2.0 • 编程工具:Wing IDE • Python版本:2.7.9 • 涉及到的主要pytho...

9443
来自专栏猿人谷

使用asp调用.net xml web services

(是不是实际上可以用这个办法调用任何xml web services呢?高人答一下) 最近在做一个web services,由我来写文档。为了方便广大asp用户...

2597
来自专栏人云亦云

Chef

3717
来自专栏IT米粉

你应该学会的接口调试神器——Postman高级用法

入门级功能,但是被很多人忽略。postman左侧有个collections的tab,可以将接口进行分组,而且可以将分组以后的接口进行批量的执行,是一个非常赞的功...

60610
来自专栏IT米粉

你应该学会的Postman用法

postman这个神器相信大家都用过,程序员作为非专业的测试人员,非常需要这么一款简单轻量级的restful测试工具,但是不知道你是否知道,postman的强大...

4957
来自专栏salesforce零基础学习

salesforce lightning零基础学习(六)Lightning Data Service(LDS)

本篇可参看:https://trailhead.salesforce.com/modules/lightning_data_service

1593
来自专栏FreeBuf

浅谈XXE攻击

0×00. 介绍 现在越来越多主要的web程序被发现和报告存在XXE(XML External Entity attack)漏洞,比如说facebook、pa...

2108
来自专栏全沾开发(huā)

NPM实用指北

NPM实用指北 npm作为下载node附送的大礼包,大家一定不会陌生。 然而关于npm,估计大量的只是用到npm install XX...

45610
来自专栏张善友的专栏

Service Fabric 与 Ocelot 集成

云应用程序通常都需要使用前端网关,为用户、设备或其他应用程序提供同一个入口点。 在 Service Fabric 中,网关可以是任意无状态服务(如 ASP.NE...

2223
来自专栏Java帮帮-微信公众号-技术文章全总结

Java企业面试——Javaweb

2.Javaweb阶段 2.1 Ajax你以前用过么?简单介绍一下 AJAX = 异步 JavaScript 和 XML。 AJAX 是一种用于创建快速...

3698

扫码关注云+社区

领取腾讯云代金券