leveldb学习之version

到此为止,基本上leveldb的主要功能组件都已经分析完了,下面如何把它们组合在一起形成一个高效稳定的数据库,这就是DBimpl类和compact进程的工作。

  • campact进程

为了均衡读写的效率,sstable文件分层次(level)管理,db预定义了最大的level值。compact负责将memtable持久化成sstable,以及均衡整个db中各level的sstable。

  • 版本控制

当执行一次compaction后,Leveldb将在当前版本基础上创建一个新版本,当前版本就变成了历史版本。在leveldb中,Version就代表了一个版本,它包括当前磁盘及内存中的所有文件信息。VersionSet是所有Version的集合,这是个version的管理机构。在所有的version中,只有一个是CURRENT。

这两部分具体的操作还是在源码中细讲吧。

static double MaxBytesForLevel(int level) {
  // Note: the result for level zero is not really used since we set
  // the level-0 compaction threshold based on number of files.
  double result = 10 * 1048576.0;  // Result for both level-0 and level-1
  while (level > 1) {
    result *= 10;
    level--;
  }
  return result;
}
static uint64_t MaxFileSizeForLevel(int level) {
  return kTargetFileSize;  // We could vary per level to reduce number of files?
}

关于不同level中sstable文件的最大大小,kTargetFileSize 是静态常量,设为2 * 1048576。

version

leveldb将每次compact后的最新数据状态定义为Version,也就是当前db信息以及每个level上具有最新数据状态的sstable集合。

class version{
  VersionSet* vset_;            // VersionSet to which this Version belongs
//表示所属的versionset,version是数据库的版本,那versionset就是version的集合

  Version* next_;               // Next version in linked list
  Version* prev_;               // Previous version in linked list
//指向前后版本的指针

  int refs_;                    // Number of live refs to this version
  // List of files per level
  std::vector<FileMetaData*> files_[config::kNumLevels];
  //每个level的所有sstable信息

  // Next file to compact based on seek stats.
  FileMetaData* file_to_compact_;
  int file_to_compact_level_;
  //需要compact的文件

  // Level that should be compacted next and its compaction score.
  // Score < 1 means compaction is not strictly needed.  These fields
  // are initialized by Finalize().
  double compaction_score_;
  int compaction_level_;
}

但是由于某些时候compact会在某个level上新加入或者删除一些sstable,如果这个时候,这些sstable正在被读,为了处理这样的读写竞争,基于sstable一旦生成就不会改动的特点,每个version加入引用计数refs_,这样db中可能有多个version同时存在,他们通过链表链接。当version的引用计数为0并且不是当前最新的version,他会从链表中移除,对应的,该version的sstable就可以删除了。

Version不会修改其管理的sstable文件,只有读取操作。先来看一个结构,FileMetaData结构:保存sstable的元信息。

struct FileMetaData {
  int refs;
  int allowed_seeks;          // Seeks allowed until compaction
  uint64_t number;
  uint64_t file_size;         // File size in bytes
  InternalKey smallest;       // Smallest internal key served by table
  InternalKey largest;        // Largest internal key served by table
  FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { }
};

smallest、largest可以帮助定位target所在文件。遍历version::file_容器数组,即可找到目标关键字所在文件。

version内定义了迭代器类LevelFileNumIterator,通过NewConcatenatingIterator成员函数返回文件迭代器,一般用于表示查找的结果。

int FindFile(const InternalKeyComparator& icmp,
             const std::vector<FileMetaData*>& files,
             const Slice& key)

完成的是在中一个level查找(使用二分法),会被version中有关定位文件的函数调用。

SomeFileOverlapsRange函数用于判断sstable的key范围是否溢出smallest_user_key和largest_user_key范围之间。

bool SomeFileOverlapsRange(
    const InternalKeyComparator& icmp,
    bool disjoint_sorted_files,
    const std::vector<FileMetaData*>& files,
    const Slice* smallest_user_key,
    const Slice* largest_user_key)

Get函数,version内的key查询操作,先看声明

Status Version::Get(const ReadOptions& options,
                    const LookupKey& k,
                    std::string* value,
                    GetStats* stats)

由于level-0的sstable是memtable直接dump到磁盘,所以可能overlap,而level-0以上全是由compact生成,不存在overlap,即不同的sstable存储的数据,key的范围没有交集,目的是为了提高读得性能。所以在函数里,对于level-0和level-n区别对待:

level-0:会遍历level-0的FileMetaData数组,并且对找到use_key的sstable文件按照从新到旧排序

level-n:则调用FindFile函数处理,找到即返回结果

发现在某个sstable找到key,则去内存中有无此table的缓存cache,并加载进内存。

s = vset_->table_cache_->Get(options, f->number, f->file_size,ikey, &saver, SaveValue);

get函数有一个GetStats参数,这是一个保存查找关键词结果的结构,内含entry的key和value以及所在的sstable的信息。leveldb会对每一次的查找做一个记录,并以查找结果更新version。

version更新函数

bool Version::UpdateStats(const GetStats& stats) {
  FileMetaData* f = stats.seek_file;
  if (f != NULL) {
    f->allowed_seeks--;
    if (f->allowed_seeks <= 0 && file_to_compact_ == NULL) {
      file_to_compact_ = f;
      file_to_compact_level_ = stats.seek_file_level;
      return true;
    }
  }
  return false;
}

如果一个sstable被频繁查寻访问(查询次数高达allowed_seeks次),则将该sstable记录成version的file_to_compact_,sstable的level也会跟新,file_to_compact_level_ = stats.seek_file_level。这个策略只是leveldb为了保持磁盘中levels-sstables结构的平衡性策略的一部分,相关函数RecordReadSample、ForEachOverlapping。

其他函数:

Ref、Unref就是前面解决读写竞争的引用计数功能。当refs_为0 时,此version即可被delete。

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

时间: 2024-10-12 12:02:28

leveldb学习之version的相关文章

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学习:skiplist

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

leveldb 学习。

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

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学习:dbimpl(1)

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

leveldb学习笔记

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

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的派生类究竟是什

leveldb 学习笔记之VarInt

在leveldb在查找比较时的key里面保存key长度用的是VarInt,何为VarInt呢,就是变长的整数,每7bit代表一个数,第8bit代表是否还有下一个字节, 比如小于128(一个字节以内)的值生成方式如下: 将该值与二进制1000 0000值进行比较,如果小于,则直接将该值作为unsigned char也就是整数值写入. 一个大于128也就是超过1个字节的变长整数的生成方式如下: 将该值与二进制1000 000 000 0000 (32768=2<<14)进行比较,如果小于,则将该值

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(