Leveldb源码解析第三篇【sstable 收尾】

来源:互联网 发布:程序员杂志app 编辑:程序博客网 时间:2024/06/06 15:46

版权声明:本文为博主原创文章,未经博主允许不得转载。

前面介绍完了table的data block和filter block,今天就来讲table收一下尾,table还剩meta index block,index block,footer

这几个都比较简单,就一起介绍了

table.h中是用来解析一个table的,在没搞懂table是什么东西前直接解析有点困难,而了解table最快速的办法就是看怎么table怎么构造的,下面直接开始构造一个table,开整

本章涉及到源码文件有

include/leveldb/table.hinclude/leveldb/table_builder.htable/table.cctable/table_builder.cctable/format.h
// table_builder.hclass TableBuilder { public:  // 构造方法  TableBuilder(const Options& options, WritableFile* file);  // 析构方法  ~TableBuilder();  // 改变配置参数  Status ChangeOptions(const Options& options);  // 在 table 中添加一个key  void Add(const Slice& key, const Slice& value);  // 当一个data block达到阈值以后调用  void Flush();  // Return non-ok iff some error has been detected.  Status status() const;  // 当table大小达到阈值后调用,主要是讲meta block,index block等信息写入到文件中  Status Finish();  // Indicate that the contents of this builder should be abandoned.  Stops  // using the file passed to the constructor after this function returns.  // If the caller is not going to call Finish(), it must call Abandon()  // before destroying this builder.  // REQUIRES: Finish(), Abandon() have not been called  void Abandon();  // 返回有多少个键值对  uint64_t NumEntries() const;  // 返回文件大小  uint64_t FileSize() const; private:  // 返回操作是否成功  bool ok() const { return status().ok(); }  // 将block写入到编码到string中  void WriteBlock(BlockBuilder* block, BlockHandle* handle);  // 将编码后的block写入到文件中  void WriteRawBlock(const Slice& data, CompressionType, BlockHandle* handle);  // rep很重要,table的所有信息都放在这个结构体中,现在还不知道为什么这么设计,  // 后面深入后在分析作者这么设计的目录  struct Rep;  Rep* rep_;  // No copying allowed  TableBuilder(const TableBuilder&);  void operator=(const TableBuilder&);};
// table_builder.cc// 先从存放table所有信息的结构体开始struct TableBuilder::Rep {  // 两个配置对象,暂时可以忽略  Options options;  Options index_block_options;  // 最后table是要写入文件的,file对象就是用来干这个事的  WritableFile* file;  // offset记录table的偏移量,主要用于index block,后续会介绍  uint64_t offset;  // 每次操作的状态  Status status;  // 存放data block  BlockBuilder data_block;  // 存放index block  BlockBuilder index_block;  // 最后一个key  std::string last_key;  // table中有多个键值对  int64_t num_entries;  // table是否结束  bool closed;          // Either Finish() or Abandon() has been called.  // 上一章讲到的  FilterBlockBuilder* filter_block;  // 这个变量表示添加的key是不是data block的第一个key  bool pending_index_entry;  BlockHandle pending_handle;  // Handle to add to index block  // data block添加到table中时会压缩block,这个变量是用来存储压缩后的内容  std::string compressed_output;  // rep构造方法  Rep(const Options& opt, WritableFile* f)      : options(opt),        index_block_options(opt),        file(f),        offset(0),        data_block(&options),        index_block(&index_block_options),        num_entries(0),        closed(false),        filter_block(opt.filter_policy == NULL ? NULL                     : new FilterBlockBuilder(opt.filter_policy)),        pending_index_entry(false) {    index_block_options.block_restart_interval = 1;  }};// 在table中添加一个key-value,也就是在table中的data block中添加一个keyvoid TableBuilder::Add(const Slice& key, const Slice& value) {  // table的所有信息全部封装在了rep_中  Rep* r = rep_;  assert(!r->closed);  if (!ok()) return;  if (r->num_entries > 0) {    assert(r->options.comparator->Compare(key, Slice(r->last_key)) > 0);  }  // pending_index_entry表示添加的key是不是data block的第一个key  if (r->pending_index_entry) {    assert(r->data_block.empty());    // 如果是的话,就要先得到上一个data block的最后一个key和当前data block的第一个key的一个中间key    // 比如last_key为abcd,key为abcg,那么中间key为abce,也就是第一个不同的字符加1    r->options.comparator->FindShortestSeparator(&r->last_key, key);    // handle_encoding保留上一个data block的偏移信息    std::string handle_encoding;    // 将上一个data block的偏移信息编码到string中    r->pending_handle.EncodeTo(&handle_encoding);    // 在index_block中添加中间key和上一个data block的偏移信息    r->index_block.Add(r->last_key, Slice(handle_encoding));    // pending_index_entry设置为false,表示后面在添加key,就不是第一个key了    r->pending_index_entry = false;  }  if (r->filter_block != NULL) {    // 在filter block的keys中添加key    r->filter_block->AddKey(key);  }  // 给last_key赋上新值  r->last_key.assign(key.data(), key.size());  r->num_entries++;  // 在data block中添加键值  r->data_block.Add(key, value);  // 得到当前data block的估值大小  const size_t estimated_block_size = r->data_block.CurrentSizeEstimate();  if (estimated_block_size >= r->options.block_size) {    // 如果大于阈值的话就要刷新了    Flush();  }}// 当一个data block结束时,就会刷新一下void TableBuilder::Flush() {  Rep* r = rep_;  assert(!r->closed);  if (!ok()) return;  if (r->data_block.empty()) return;  assert(!r->pending_index_entry);  // 将data block写入到文件中  WriteBlock(&r->data_block, &r->pending_handle);  if (ok()) {    // 为下一个data block作准备    r->pending_index_entry = true;    r->status = r->file->Flush();  }  if (r->filter_block != NULL) {    r->filter_block->StartBlock(r->offset);  }}void TableBuilder::WriteBlock(BlockBuilder* block, BlockHandle* handle) {  // File format contains a sequence of blocks where each block has:  //    block_data: uint8[n]  //    type: uint8  //    crc: uint32  assert(ok());  Rep* r = rep_;  // 当前得到的block里面只是存储了key-value,真正完成一个block还需要在后面写入重启点等信息  raw是整个block(包括重启点)放到string里面的信息  Slice raw = block->Finish();  Slice block_contents;  // data block是否需要压缩  CompressionType type = r->options.compression;  // TODO(postrelease): Support more compression options: zlib?  switch (type) {    case kNoCompression:    // 不需要压缩的话直接向block的内容赋值给block_contents      block_contents = raw;      break;    case kSnappyCompression: {      // 如果需要压缩的话,会将压缩后的信息放到compressed      std::string* compressed = &r->compressed_output;      // 如果需要压缩,压缩率高于12.5%的话就压缩      if (port::Snappy_Compress(raw.data(), raw.size(), compressed) &&          compressed->size() < raw.size() - (raw.size() / 8u)) {        block_contents = *compressed;      } else {        // 否则就直接不压缩了,并将type设置为不压缩        // Snappy not supported, or compressed less than 12.5%, so just        // store uncompressed form        block_contents = raw;        type = kNoCompression;      }      break;    }  }  // 将block的内容放到table中  WriteRawBlock(block_contents, type, handle);  // 将压缩后的内容设置为空  r->compressed_output.clear();  // block也需要重置  block->Reset();}void TableBuilder::WriteRawBlock(const Slice& block_contents,                                 CompressionType type,                                 BlockHandle* handle) {  Rep* r = rep_;  // 将当前block的偏移和block的内容放到handle中  handle->set_offset(r->offset);  handle->set_size(block_contents.size());  // 将block的内容写入到文件中  r->status = r->file->Append(block_contents);  if (r->status.ok()) {    // block的结尾标识,有5个字符    char trailer[kBlockTrailerSize];    // 第一个字符记录的是否压缩    trailer[0] = type;    // 算出block的crc值    uint32_t crc = crc32c::Value(block_contents.data(), block_contents.size());    crc = crc32c::Extend(crc, trailer, 1);  // Extend crc to cover block type    // 将crc值放到后4个字符中    EncodeFixed32(trailer+1, crc32c::Mask(crc));    // 将结尾标识放到block后面    r->status = r->file->Append(Slice(trailer, kBlockTrailerSize));    if (r->status.ok()) {      // r的偏移就要加上block的大小和结尾标识的大小了      r->offset += block_contents.size() + kBlockTrailerSize;    }  }}Status TableBuilder::status() const {  return rep_->status;}// 上述代码中都只是在文件中写入data block的内容,当table结束时就需要写入filter,index等信息了Status TableBuilder::Finish() {  Rep* r = rep_;  Flush();  assert(!r->closed);  r->closed = true;  // meta block就是filter block  BlockHandle filter_block_handle, metaindex_block_handle, index_block_handle;  // Write filter block  // 将filter block不压缩,写入到文件中  if (ok() && r->filter_block != NULL) {    WriteRawBlock(r->filter_block->Finish(), kNoCompression,                  &filter_block_handle);  }  // Write metaindex block  // meta index block只存了一个key-value,key是filter使用的算法名字,value是filter block的偏移信息  // 作用是在table中快速定位到filter block  if (ok()) {    BlockBuilder meta_index_block(&r->options);    if (r->filter_block != NULL) {      // Add mapping from "filter.Name" to location of filter data      std::string key = "filter.";      key.append(r->options.filter_policy->Name());      std::string handle_encoding;      filter_block_handle.EncodeTo(&handle_encoding);      meta_index_block.Add(key, handle_encoding);    }    // TODO(postrelease): Add stats and other meta blocks    // 将meta index block写入到file中    WriteBlock(&meta_index_block, &metaindex_block_handle);  }  // Write index block  //   if (ok()) {    if (r->pending_index_entry) {      // 如果最后一个key是最后data block的第一个key的话,需要将这个block的偏移写到index block中      // 和Add函数类似      r->options.comparator->FindShortSuccessor(&r->last_key);      std::string handle_encoding;      r->pending_handle.EncodeTo(&handle_encoding);      r->index_block.Add(r->last_key, Slice(handle_encoding));      r->pending_index_entry = false;    }    // 将index block写入到文件中    WriteBlock(&r->index_block, &index_block_handle);  }  // Write footer  if (ok()) {    // footer里面记录了index block和meta index block的偏移信息    Footer footer;    // 将meta index block的偏移信息放在footer中    footer.set_metaindex_handle(metaindex_block_handle);    // 将index block的偏移信息放在footer中    footer.set_index_handle(index_block_handle);    std::string footer_encoding;    // 将footer信息编码到string中    footer.EncodeTo(&footer_encoding);    // 然后将footer_encoding写入到文件中    r->status = r->file->Append(footer_encoding);    if (r->status.ok()) {      r->offset += footer_encoding.size();    }  }  return r->status;}

【作者:antonyxu https://antonyxux.github.io/ 】

原创粉丝点击