leveldb学习之sstable(2)

block写入:block_builder

block.h和.cc里定义了block的entry存储格式和restart,提供了entry的查找接口以及迭代器。那么如何往写block里写entry呢?leveldb遵循面向对象的设计理念在block_builder类里提供了相关接口。

BlockBuilder相关函数:

  • Add( )将entry顺序写入现有block数据块的末端,排序工作在上层的函数完成。
  • Finish( ),当block写满,完成写入重启点数组和重启点个数的写入
  • Reset( ),重置block

sstable

已经知道,sstable是leveldb中持久化数据的文件格式。而整体来看,sstable由数据(data)和元信息(meta/index)组成,数据和源信息统一以block单位存储,读取时也按统一的逻辑读取,整体的数据格式如下:

  • data_block:实际存储的kv数据
  • meta_block:当前版本未实现
  • index_block:保存每个data_block的last_key及其在sstable文件中的索引

sstable读取:table

/table/table.cc是有关将sstable读取的操作:

 private:
  struct Rep;
  Rep* rep_;

定义了结构rep,并在table类设立一个指针成员。并在table::open( )函数完成了rep_的实例化

Rep* rep = new Table::Rep;

rep结构:

struct Table::Rep {
  ~Rep() {
    delete filter;
    delete [] filter_data;
    delete index_block;
  }

  Options options;//用户设置
  Status status;//状态
  RandomAccessFile* file;//文件读操作流,主要成员有文件的名字,i节点和读操作
  uint64_t cache_id;
  FilterBlockReader* filter;//和meta_block有关,不用管
  const char* filter_data;//

  BlockHandle metaindex_handle;  // Handle to metaindex_block: saved from footer
  Block* index_block;
};

BlockHandle是一个用来指向block在文件中位置的“指针”(里面记录的是文件偏移量),可参考format.h;

footer:文件末尾的固定长度的数据,保存着metaindex_block和index_block的索引信息(blockHandle),最后有8字节的magic校验。显然footer信息的读取对掌握整个table至关重要。

在table::open( )函数中就会从文件的末尾读取footer:

......
  Slice footer_input;
  Status s = file->Read(size - Footer::kEncodedLength, Footer::kEncodedLength,
                        &footer_input, footer_space);
  if (!s.ok()) return s;

  Footer footer;
  s = footer.DecodeFrom(&footer_input);
  if (!s.ok()) return s;

......
  Block* index_block = NULL;
  if (s.ok()) {
    s = ReadBlock(file, opt, footer.index_handle(), &contents);
    if (s.ok()) {
      index_block = new Block(contents);
    }
  }

  ......
    rep->file = file;
    rep->metaindex_handle = footer.metaindex_handle();
    rep->index_block = index_block;

readBlock就是通过blockhandle读取文件中指定block的函数,定义在format.cc

Status ReadBlock(RandomAccessFile* file,
                 const ReadOptions& options,
                 const BlockHandle& handle,
                 BlockContents* result) {
  result->data = Slice();
  result->cachable = false;
  result->heap_allocated = false;
//blockcontents的初始化

  // Read the block contents as well as the type/crc footer.
  // See table_builder.cc for the code that built this structure.
  size_t n = static_cast<size_t>(handle.size());
  char* buf = new char[n + kBlockTrailerSize];
  Slice contents;
  Status s = file->Read(handle.offset(), n + kBlockTrailerSize, &contents, buf);
  if (!s.ok()) {
    delete[] buf;
    return s;
  }
  if (contents.size() != n + kBlockTrailerSize) {
    delete[] buf;
    return Status::Corruption("truncated block read");
  }

  // do something
  return Status::OK();
}

kBlockTrailerSize就是每个block末端的五字节信息,包括压缩标志位和用于CRC校验的开销。do something 就是对提取到的内容分析,判断有无压缩,错误时返回状态信息以及赋值result。

sstable写入:table_builder

sstable写如不需要关心排序,因为sstable的产生是由memtable dump或者compact时merge排序产生的,key的顺序上层已经保证。

结构rep:

struct TableBuilder::Rep {
  Options options;
  Options index_block_options;
  WritableFile* file;//封装了流操作的文件
  uint64_t offset;//写入位置的偏移量
  Status status;
  BlockBuilder data_block; // 用于将entry写入当前data_block
  BlockBuilder index_block;// 用于在index_block添加data_block的索引信息
  std::string last_key; //当前table中最后条目的key,写入key要大于此,否则上层未提供排好序的entry
  int64_t num_entries;  //条目总数
  bool closed;          //table关闭标志位, Either Finish() or Abandon() has been called.
  FilterBlockBuilder* filter_block;

  bool pending_index_entry;//当前block为空时为true
  BlockHandle pending_handle;  // Handle to add to index block

  std::string compressed_output;

  Rep();
  }
void TableBuilder::Add(const Slice& key, const Slice& value)
{
  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);
  }
    ......
  r->last_key.assign(key.data(), key.size());
  r->num_entries++;
  r->data_block.Add(key, value);

  const size_t estimated_block_size = r->data_block.CurrentSizeEstimate();
  if (estimated_block_size >= r->options.block_size) {
    Flush();
  }
}

leveldb把数据dump到磁盘,在内存中只有一份block,当block满了(大于options.block_size),就自动将此block写入磁盘(Flush)。

写入操作调用层次:

  • Add( ),写入内存中的block,判断block大小,决定是否写入磁盘
  • Flush( )
  • WriteBlock( ),取block压缩标志位决定是否压缩,写入压缩标志位
  • WriteRawBlock( ),添加CRC,调用文件流写入磁盘

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

时间: 2024-08-09 22:01:55

leveldb学习之sstable(2)的相关文章

leveldb学习:sstable(2)

block写入:block_builder block.h和.cc里定义了block的entry存储格式和restart,提供了entry的查找接口以及迭代器.那么怎样往写block里写entry呢?leveldb遵循面向对象的设计理念在block_builder类里提供了相关接口. BlockBuilder相关函数: Add( )将entry顺序写入现有block数据块的末端,排序工作在上层的函数完毕. Finish( ).当block写满,完毕写入重新启动点数组和重新启动点个数的写入 Res

LevelDb学习资料

LevelDb学习资料 标签(空格分隔): db,k/v_db 以下是leveldb的介绍资料 初识LevelDb 整体架构 log文件 SSTable文件 MemTable 写入与删除记录 如何根据Key读取记录? Compaction levelDb中的Cache LevelDB性能分析和表现 levelDB tutorial LevelDB 网址:http://leveldb.org/ 基于levelDB做优化的RocksDB,由facebook维护开发,仍然在更新,其网址:http://

leveldb学习笔记

LevelDB由 Jeff Dean和Sanjay Ghemawat开发. LevelDb是能够处理十亿级别规模Key-Value型数据持久性存储的C++ 程序库. 特别如下: 1.LevelDb是一个持久化存储的KV系统,将大部分数据存储到磁盘上. 2.LevleDb在存储数据时,是根据记录的key值有序存储的,应用可以自定义key大小比较函数. 3.LevelDb的操作接口包括写记录,读记录以及删除记录.针对多条操作的原子批量操作. 4.LevelDb支持数据快照(snapshot)功能,使

leveldb 学习。

1)大概浏览了leveldb文档的介绍.本想逐步看代码,想想还是自己先实现一个看看如何改进. 2)完成了一个非常丑陋的初版,但是还是比初初版有进步. 3)key value的数据库,不允许有key重复,所以必须检测key. 1,插入检测key重复,太耗时间,不可能去检查数据文件.明显必须加入一个索引文件.形式key,offset. 2,  key,offset的索引形式,数据到达5w,简直不可忍受.插入数据时,必须对索引文件排序,之后可以二分法查找key. 3,排序,二分查找法,又必须要求可以对

leveldb学习:skiplist

leveldb中的memtable仅仅是一个封装类,它的底层实现是一个跳表. 跳表是一种基于随机数的平衡数据结构.其它的平衡数据结构还有红黑树.AVL树.但跳表的原理比它们简单非常多.跳表有点像链表,仅仅只是每一个节点是多层结构,通过在每一个节点中添加向前的指针提高查找效率.例如以下图: 在/leveldb/db目录下有跳表的实现skiplist.h和跳表的測试程序skiplist_test.cc. template<typename Key, class Comparator> class

leveldb学习:versionedit和versionset

VersionEdit: compact过程中会有一系列改变当前Version的操作(FileNumber增加,删除input的sstable,增加输出的sstable),为了缩小version切换的时间点,将这些操作封装成versionedit,compact完成时,将versionedit中的操作一次应用到当前version即可得到最新状态的version. versionedit的成员变量: private: friend class VersionSet; typedef std::se

leveldb学习之version

到此为止,基本上leveldb的主要功能组件都已经分析完了,下面如何把它们组合在一起形成一个高效稳定的数据库,这就是DBimpl类和compact进程的工作. campact进程 为了均衡读写的效率,sstable文件分层次(level)管理,db预定义了最大的level值.compact负责将memtable持久化成sstable,以及均衡整个db中各level的sstable. 版本控制 当执行一次compaction后,Leveldb将在当前版本基础上创建一个新版本,当前版本就变成了历史版

leveldb学习:dbimpl(1)

leveldb将数据库的有关操作都定义在了DB类,它负责整个系统功能组件的连接和调用,是整个系统的脊柱. level::DB是一个接口类,真正的实现在DBimpl类. 作者在文档impl.html中描述了leveldb的实现,其中包括文件组织.compaction和recovery等等. DBimpl的成员变量包括:字符比较器internal_comparator_.配置类options_.bool型状态量.string型DB库名.cache对象.memtable对象.versionset对象等

leveldb学习:Cache

leveldb自己实现了cache缓冲区替代算法,参见代码cache.h和cache.c文件.leveldb中table_cache等都是以class cache作为底层实现. cache.h中,我们看到cache类是一个抽象类,声明了lookup;insert;release;value;erase等函数,同时声明了一个全局函数 extern Cache* NewLRUCache(size_t capacity); 用来构造cache派生类对象,并返回派生类指针.那么cache的派生类究竟是什