[LevelDB] LevelDB之WriteBatch

1. LevelDB的写数据过程

在LevelDB中,我们通过调用db->Put(WriteOptions(),&key,&value)来写入数据,而WriteOptions只有一个变量sync,且默认初始值为false,因此leveldb默认的写数据方式是异步,即每将写操作提交将数据写入到内存中就返回,而将数据从内存写到磁盘的方式是异步的。异步写比同步写的效率高得多,但是当系统故障时,可能导致最近的更新丢失。若将WriteOptions的sync设为true,则每次写入都会将数据写入到磁盘中,速度非常慢。

为此,LevelDB使用WriteBatch来替代简单的异步写操作,首先将所有的写操作记录到一个batch中,然后执行同步写,这样同步写的开销就被分摊到多个写操作中,降低同步写入的成本。

利用leveldb的WriteBatch可以进行批量处理,用法如下:

1 leveldb::WriteBatch batch;
2 batch.Put(key1, value1);
3 batch.Delete(key1);
4 batch.Put(key2, value);
5 s = db->Write(leveldb::WriteOptions(), &batch);

即使没有显式使用batch,在调用DB::Put()时,也是将一条记录通过WriteBatch的形式存储到系统中的。

 

2. WriteBatch

WriteBatch的原理是先将所有的操作记录下来,然后再一起操作。由之前的分析可知,LevelDB插入和删除数据并不是直接插入、删除数据,而是插入一条记录(由记录的类型标志位来确定是要插入数据还是删除数据,具体的插入、删除操作将在后台Compaction时进行)。

因此一个WriteBatch将会记录许多个操作,每一个操作代表着要插入或删除相应数据。

WriteBatch类的定义如下:

 1 class WriteBatch {
 2 public:
 3     WriteBatch();
 4     ~WriteBatch();
 5     void Put(const Slice& key, const Slice& value);  // 追加一条要插入新数据的请求
 6     void Delete(const Slice& key);                // 追加一条要删除记录的请求 7
 8     // Clear all updates buffered in this batch.
 9     void Clear() {    
10       rep_.clear();         // clear string object of rep_
11       rep_.resize(kHeader);  // equal to new char[KHeader]
12     }
13 
14     // Support for iterating over the contents of a batch.
15     class Handler {
16     public:
17       virtual ~Handler();
18       virtual void Put(const Slice& key, const Slice& value) = 0;
19       virtual void Delete(const Slice& key) = 0;
20     };
21 
22     Status Iterate(Handler* handler) const;
23 
24 private:
25     friend class WriteBatchInternal;   // so this class can access rep_
26     std::string rep_;            // string buffer object used to save all requests
27 };

每一个WriteBatch都是以一个固定长度的头部开始,然后后面接着许多连续的记录,固定头部共12字节,其中前8字节为WriteBatch的序列号,对应rep_[0]到rep_[7],每次处理Batch中的记录时才会更新,后四字节为当前Batch中的记录数,对应rep_[8]到rep_[11];通过头部我们就可以知道每一个batch的Sequence Number和batch中的总记录数,将batch写入到文件中后就可以方便的依次获取每一条记录了。

后面的记录结构为

  插入新数据:type(kTypeValue、kTypeDeletion),Key_size,Key,Value_size,Value 

  删除就数据:type(kTypeValue、kTypeDeletion),Key_size,Key

 

3. WriteBatch的基本操作

WriteBatch的基本操作是记录一个要插入或删除某个数据的操作,最基本的操作就是Put和Delete。

Put:将一条要插入数据的操作写入到rep_中

void WriteBatch::Put(const Slice& key, const Slice& value) {
     WriteBatchInternal::SetCount(this, riteBatchInternal::Count(this) + 1);  //操作总数加1
     rep_.push_back(static_cast<char>, (kTypeValue)); //写入操作类型
     PutLengthPrefixedSlice(&rep_, key);         //写入key.size()和key.data()
     PutLengthPrefixedSlice(&rep_, value);        //写入value.size()和        value.data()
}

Delete:将一条要删除数据的操作写入到rep_中

void WriteBatch::Delete(const Slice& key) {
      WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1); //操作总数加1
      rep_.push_back(static_cast<char>(kTypeDeletion));     //写入操作类型
      PutLengthPrefixedSlice(&rep_, key);              //写入待删除数据的key值
}

 

4. 基本示例

WriteBatch wb;
wb.Put("key1","hello");
wb.Put("key2","hi");
wb.Delete("key1");

执行上面的代码后,WriteBatch中的rep_如下:

10000000 3000 (前8位为序列号,从1开始,后4位为操作数,共3个操作)
0   4   key1   5   h e l l o (第一个操作,0表示要插入数据)
0   4   key2   2   h i   (第二个操作)
1   4   key1            (第三个操作,1表示要删除数据) 

posted @ 2018-11-09 12:17  MH1092  阅读(1218)  评论(0)    收藏  举报