从【leveldb源码分析--插入删除流程】和WriteBatch其名我们就很轻易的知道,这个是leveldb内部的一个批量写的结构,在leveldb为了提高插入和删除的效率,在其插入过程中都采用了批量集合相邻的多个具有相同同步设置的写请求以批量的方式进行写入。

其成员变量仅包含了一个  std::string 类型的 rep_变量,其Put和Delete(其实也是插入删除操作,而非删除Put进去的数据,或者你可以将其理解为Put Delete operation的过度简写)都将相应的操作Encode后存入其中。我们来看看其encode的格式

WriteBatch::rep_ :=  [ sequence: fixed64 |  count: fixed32 |  data: record[count ]
record := [ kTypeValue | key(varint32 | data) | value(varint32 | data) ]

我们首先看一下WriteBatch内部相关的一些结构和成员

class Handler {
public:
virtual ~Handler();
virtual void Put(const Slice& key, const Slice& value) = ;
virtual void Delete(const Slice& key) = ;
};
Status Iterate(Handler* handler) const;
friend class WriteBatchInternal;

WriteBatchInternal主要是对WriteBatch的内部解码编码的封装,简化WriteBatch的结构。而Handler接口我们可以看到MemTableInserter成为了其一个实现,这个handler是在WriteBatch的内部解码遍历过程中逐个调用Handler对应的Put、Delete到MemTable中。所以再回头分析DBImpl::BuildBatchGroup方法。

// REQUIRES: First writer must have a non-NULL batch
WriteBatch* DBImpl::BuildBatchGroup(Writer** last_writer) {
Writer* first = writers_.front();
WriteBatch* result = first->batch;
size_t size = WriteBatchInternal::ByteSize(first->batch);

  /*  这里主要是设置每次批量写入的最大的数据量,防止一次插入过多数据导致等待写完成的时间过长       因为从Write的逻辑分析中我们知道只有位于队列首的写线程会去批量组装然后执行真正的插入,
    其他的线程都是在等待这个批量写的完成。 */
size_t max_size =  << ;
if (size <= (<<)) {
max_size = size + (<<);
} *last_writer = first;
std::deque<Writer*>::iterator iter = writers_.begin();
++iter; // 跳过 "first"
for (; iter != writers_.end(); ++iter) {
Writer* w = *iter;
if (w->sync && !first->sync) {
// 只组合相邻的sync设置相同的操作到一批进行处理.
break;
} if (w->batch != NULL) {
size += WriteBatchInternal::ByteSize(w->batch);
if (size > max_size) {
// 如果总大小超过设置的现在大小,不再继续组装过程,跳出循环执行已组装的请求
break;
} // 第一次进入循环,把tmp_batch_赋给result并把第一个放入其中,后继的都是append到tmp_batch_中
       最后return出去进行操作的也是这个tmp_batch_
if (result == first->batch) {
result = tmp_batch_;
assert(WriteBatchInternal::Count(result) == );
WriteBatchInternal::Append(result, first->batch);
}
WriteBatchInternal::Append(result, w->batch);
}
*last_writer = w;
}
return result;
}

接下来对该batch进行操作的是WriteBatchInternal::InsertInto(updates, mem_)这个调用,这就是前面提到的采用了一个Iterator的方式解码遍历操作batch中的数据

Status WriteBatchInternal::InsertInto(const WriteBatch* b,
MemTable* memtable) {
MemTableInserter inserter;
inserter.sequence_ = WriteBatchInternal::Sequence(b);
inserter.mem_ = memtable;
return b->Iterate(&inserter);
}
Status WriteBatch::Iterate(Handler* handler) const {
while (!input.empty()) {
switch (tag) {
case kTypeValue:
handler->Put(key, value);
break;
case kTypeDeletion:
handler->Delete(key);
    break;
}
}
return Status::OK();
}

这里我们再回顾一下Write有

while (!w.done && &w != writers_.front()) {

这个语句是说线程一直等待直到当前线程的请求被完成或者请求到队列的最前端,那么为什么leveldb采用了这样的其中一个线程去批量操作而其他线程进行等待的方式呢?我们知道leveldb在实际插入过程中会有一系列的判断,和写日志到磁盘的操作。而首先这些判断都是在保持锁的情形下进行的,这里其实也就注定了只能是串行的;其次对于写磁盘,将多次写合并为一次写入会显著的提高效率,相当于 N * 磁盘寻址时间 + 写入时间总和变为了 一次磁盘寻址时间 + 写入时间总和,即节省了(N - 1)次磁盘寻址时间。

leveldb源码分析--WriteBatch的更多相关文章

  1. leveldb源码分析--SSTable之block

    在SSTable中主要存储数据的地方是data block,block_builder就是这个专门进行block的组织的地方,我们来详细看看其中的内容,其主要有Add,Finish和CurrentSi ...

  2. leveldb源码分析--Key结构

    [注]本文参考了sparkliang的专栏的Leveldb源码分析--3并进行了一定的重组和排版 经过上一篇文章的分析我们队leveldb的插入流程有了一定的认识,而该文设计最多的又是Batch的概念 ...

  3. Leveldb源码分析--1

    coming from http://blog.csdn.net/sparkliang/article/details/8567602 [前言:看了一点oceanbase,没有意志力继续坚持下去了,暂 ...

  4. leveldb源码分析--日志

    我们知道在一个数据库系统中为了保证数据的可靠性,我们都会记录对系统的操作日志.日志的功能就是用来在系统down掉的时候对数据进行恢复,所以日志系统对一个要求可靠性的存储系统是极其重要的.接下来我们分析 ...

  5. leveldb源码分析之Slice

    转自:http://luodw.cc/2015/10/15/leveldb-02/ leveldb和redis这样的优秀开源框架都没有使用C++自带的字符串string,redis自己写了个sds,l ...

  6. LevelDB源码分析--Cache及Get查找流程

    本打算接下来分析version相关的概念,但是在准备的过程中看到了VersionSet的table_cache_这个变量才想起还有这样一个模块尚未分析,经过权衡觉得leveldb的version相对C ...

  7. leveldb源码分析--SSTable之TableBuilder

    上一篇文章讲述了SSTable的格式以后,本文结合源码解析SSTable是如何生成的. void TableBuilder::Add(const Slice& key, const Slice ...

  8. leveldb源码分析之内存池Arena

    转自:http://luodw.cc/2015/10/15/leveldb-04/ 这篇博客主要讲解下leveldb内存池,内存池很多地方都有用到,像linux内核也有个内存池.内存池的存在主要就是减 ...

  9. LevelDB源码分析-Write

    Write LevelDB提供了write和put两个接口进行插入操作,但是put实际上是调用write实现的,所以我在这里只分析write函数: Status DBImpl::Write(const ...

随机推荐

  1. rabbitmq实现一台服务器同时给指定部分的consumer发送消息(tp框架)(第六篇)

    previous article:  http://www.cnblogs.com/spicy/p/7989717.html 上一篇学习了,发送消息的时候用direct类型的exchange,绑定不同 ...

  2. Mysql 5.7版本安装:mysql 服务无法启动。

    一.解压文件 下载好MySQL后,解压到D盘下,也可以根据个人喜好解压在其他盘符的路径下,解压后的路径是:D:\mysql-5.7.17-winx64.解压好后不要太兴奋,需要配置默认文件呢! 二. ...

  3. Android 开发工具类 11_ToolFor9Ge

    1.缩放/ 裁剪图片: 2.判断有无网络链接: 3.从路径获取文件名: 4.通过路径生成 Base64 文件: 5.通过文件路径获取到 bitmap: 6.把 bitmap 转换成 base64: 7 ...

  4. ES6-Object

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  5. Xshell用鼠标选中一段文字后自动换行的问题

    JavaScript   HTML(CSS) ASP 跨浏览器开发 IIS Apache vbScript JavaScript 应用服务器 XML/XSL 其他 CGI Ajax 非技术区 Cold ...

  6. Velocity学习笔记

    一.为什么要使用velocity? 很多人下载了EasyJWeb的开源应用示例,但是对动态页面模板文件中的标签使用不是很熟悉,这里简单介绍一下.EasyJWeb特定把视图限定为Velocity,因为我 ...

  7. APS审核经验+审核资料汇总——计算机科学与技术专业上海德语审核

    1.APS是什么 德国驻华使馆文化处留德人员审核部(简称APS)成立于2001年7月,是由德国驻华使馆文化处和德意志学术交流中心(DAAD)在北京共同合作成立的服务机构. APS是中国学生前往德国留学 ...

  8. JAVA-4NIO之Channel之间的数据传输

    转载:自并发编程网ifeve.com 在Java NIO中,如果两个通道中有一个是FileChannel,那你可以直接将数据从一个channel(译者注:channel中文常译作通道)传输到另外一个c ...

  9. Linux进程间通信 -- 管道(pipe)

    前言    进程是一个独立的资源管理单元,不同进程间的资源是独立的,不能在一个进程中访问另一个进程的用户空间和内存空间.但是,进程不是孤立的,不同进程之间需要信息的交互和状态的传递,因此需要进程间数据 ...

  10. asdfasdfasdfasdf