从【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. Eclipse-离线安装Memory Anlysis Tool

    Eclipse版本:Version: Mars.2 Release (4.5.2) 1.从官网下载MAT Memory Anlysis Tool http://www.eclipse.org/mat/ ...

  2. mysql 一个表内根据字段对应值不同查询统计总数

  3. 基于flex的不定个数的按钮组

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

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

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

  5. 面试题26:合并k个排好序的单链表

    Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. c ...

  6. Linux套接字和I/O模型

    目录 1.       socket套接字的属性.地址和创建 2.       如何使用socket编写简单的同步阻塞的服务器/客户端 3.       理解Linux五种I/O模型 1.socket ...

  7. Node.js数据流Stream之Duplex流和Transform流

    Duplex流一个很好的例子是TCP套接字连接.需要实现_read(size)和_Write(data,encoding,callback)方法. var stream = require('stre ...

  8. 单击GridView进入编辑模式

    一直以来,Insus.NET在实现GridView编辑时,均是在每笔记录第一列或是最后一列放置编辑铵钮,点击编辑铵钮之后,进行编辑模式.本博文是使用另外方式,即是点击GridView记录行任一位置,进 ...

  9. redis使用场景之位操作(大数据处理)

    在学习redis的过程了,看到了redis还能用于大数据处理,具体场景如下: 腾讯10亿用户,要几个毫秒内查询到某个用户是否在线,你能怎么做?千万别说给每个用户建立一个key,然后挨个记(你可以算一下 ...

  10. 图片切换(手动切换,imagelist的单独使用)

    //当前图片的索引 private int currIndex = 0; //判断上一个按钮和下一个按钮是否可用 private void SetEabled() { //如果当前显示的是第一张图片, ...