[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表示要删除数据)

浙公网安备 33010602011771号