leveldb学习2

无论index block还是data block两者的内部结构都相同(都是block的结构),
均使用block的Seek方法进行二分查找,根据重启点进行二分查找
查找key先从index block查找,再从data block查找
index block的key是两个data block的分割点,大于等于当前block的所有key,小于下一个block的最小key
value是对应data block地址的偏移量,根据这个value可定位data block两者的内部结构都相同
data block存放的是真实数据的key,value

先通过index_iter_找到第一个 >= target 的 key, 对应的 value 是某个 data block 的 size&offset,接下来继续在该 data block 内查找。

为什么可以直接在这个 data block 内查找?我们看下原因
首先,(index_iter_ - 1).key() < target,而index_iter_ - 1对应的 data block 内所有的 key 都满足 <= (index_iter_ - 1)->key(),因此该 data block1 内所有的 key 都满足< target
其次,index_iter_.key() >= target,而index_iter_对应的 data block2 内所有的 key 都满足 <= index_iter_->key()
即data block2 内所有的 key 都满足 <= target
由于index block的key是两个data block的分割点,大于等于当前block的所有key,小于下一个block的最小key
index_iter + 1对应的 data block3 的最小key必大于index_iter_.key(),而index_iter_.key() >= target,
即data block3 的最小key大于target,由于data block1,2,3是连续,因此,如果 target 存在于该 sstable,那么一定存在于index_iter_当前指向的 data block

Status TableBuilder::Finish() 
写入filter_block_handle, metaindex_block_handle, index_block_handle

footer的编码和解码
void Footer::EncodeTo(std::string* dst) const
Status Footer::DecodeFrom(Slice* input) 

查找分隔点
void TableBuilder::Add(const Slice& key, const Slice& value) 
r->options.comparator->FindShortestSeparator
刚写入了一个data block后设置r->pending_index_entry为true

将数据写入sstable
Status BuildTable(const std::string& dbname, Env* env, const Options& options,
                  TableCache* table_cache, Iterator* iter, FileMetaData* meta)
                  
                  
对 Version 的修改主要发生于 compaction 之后。compaction 分为 minor compaction 和 major compaction,其中 minor compaction 是落 memtable 到 L0,只会新增文件,而 major compaction 会跨 level 做合并,既新增文件也删除文件。每当这时,便会生成一个新的 VersionEdit 产生新的 Version,插入 VersionSet 链表的头部

什么时候删除Version
Version含有引用计数,当引用计数为0时,会delete掉执行析构函数
析构函数里面会先从VersionSet里面删除对应的Version
然后检查Version里面包含的文件里面的引用计数,如果引用计数为0,则删除

 uint64_t new_log_number = versions_->NewFileNumber()
  logfile_number_ = new_log_number;
  
  void DBImpl::CompactMemTable() // 549 
  
  
  VersionSet和VersionEdit含有log_number_
  
  
  MakeRoomForWrite会执行MaybeScheduleCompaction
  
  MakeRoomForWrite => MaybeScheduleCompaction => BGWork => BackgroundCall => BackgroundCompaction 
  => CompactMemTable => RemoveObsoleteFiles
  
  void DBImpl::CompactMemTable()    edit.SetLogNumber(logfile_number_);  // Earlier logs no longer needed
  当成功将immetable写入0层sstable时,将versionEdit的logNumber设置为最新(即Db的log_number_),用于后面删除不需要保留的logFile
  
    RecoverLogFile根据logFile恢复丢失数据
    
    跳表比较器 Status DBImpl::MakeRoomForWrite(bool force)  mem_ = new MemTable(internal_comparator_);
    comparator.cc
    
    
    Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) 
    
    
    WriteBatch::Put => Status DBImpl::Write
    
    压缩时sstable的选择
    PickLevelForMemTableOutput  memtable dump 出来 sstable 的选择
    
    如何确定 Compaction 的输入源
    PickCompaction()
    
    第0层新增文件时比较version_set.cc BySmallestKey
    
    Status TableBuilder::Finish() 写入metaindexblock和indexblock
    Status DBImpl::WriteLevel0Table => Status BuildTable => Status TableBuilder::Finish
    
    一个sstable有一个index block和多个data block
    当写入数据时,data block大于4M时,会持久化磁盘,并更新index block
    
    void BlockBuilder::Add 把每个key,value写入buffer_,并更新restart_
    
    restarts_数组存放每个key-value的大小
    data+restart_ 第一个重启点的偏移量, 加上restarts_数组每个元素的大小即可得出每个重启点的偏移量
    
    index block和data block里每16个元素设置一个重启点,每个重启点会写入当前buffer_大小(即当前data block已写入key_value的大小)
    当data block大于4M时,会持久化磁盘,清空buffer_
    
    ReuseManifest
    
    
    Status Table::Open 从sstable读取footer和index block
    
    写入sstable时,先将key-value写入data block(先写入data block),然后写meta index block,再写index block,最后写footer
    
    DBImpl::Recover => DBImpl:RecoverLogFile => DBImpl:WriteLevel0Table => VersionSet::NewFileNumber
    
    启动时next_file_number_递增的顺序:
    VersionSet::Recover(next_file_number_ = next_file + 1) => DBImpl::WriteLevel0Table(NewFileNumber) =>
    DBImpl::Recover(MarkFileNumberUsed)
    
    f->allowed_seeks = static_cast<int>((f->file_size / 16384U)); // file_size/16KB
    16kb的计算:
    1MB/25=40KB
    40KB/25=16
    
    LevelDB:Compaction(为什么stats->seek_file在查找key时只记录一次,即如果查找的文件多于一个,则记录下第一个查找的文件)
    https://www.jianshu.com/p/527c3a00f79e?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
    
    SeekToRestartPoint:找到重启点指向的key
    NextEntryOffset:获取下一个key的位置,如果刚调用过SeekToRestartPoint,此时返回第一条entry,SeekToRestartPoint传入的size_为0
    
    基于leveldb开源的项目 ssdb rocksdb
    
    leveldb current的作用:
    current文件用于指向当前使用的MANIFEST,因为每次启动时会生成新的MANIFEST,这时候会有多个MANIFEST,需要确定使用哪一个MANIFEST,
    断电是可能发生的,当断电时可能新生成的MANIFEST还没有完全写入,这时候current仍指向旧的MANIFEST,当MANIFEST完全写入的时候,会将
    current指向新MANIFEST,也可能MANIFEST完全写入了,但current还没来得及更新记录就断电,这时候仍指向旧的MANIFEST
    
    在选中 level n+1 层的 sstable 后,可能还可以加入一些 level n 的 sstable,但为什么不扩大level n+1 层的范围呢
    因为compaction主要删除重复的数据,而选中 level n+1 层的 sstable后已包含所有和leven n层重叠的数据,扩大level n+1 层的范围并
    不会增加重复的数据,只增加compaction的文件从而更耗时
    
    MaybeScheduleCompaction的调用时机:
    Status DBImpl::Get
    Status DBImpl::MakeRoomForWrite
    void DBImpl::BackgroundCall() (Compaction会增加level n+1层所占的总字节数,有可能需要再次Compaction)
    
    300     100101100
    172     10101100
    2       00000010
    
    10101100 00000010
    
    Version析构函数可以直接delete f(FileMetaData)的原因:
    f持有引用计数,它会被多个Version引用,当引用计数为0时,即代表没有Version引用它(或引用它的Version已析构)
    故可以delte释放内存
    
    将manifes(多个VersionEdit)应用到Builder: Status VersionSet::Recover
    
    
    Status VersionSet::LogAndApply:
    新建Version,持有VersionSet的指针,新建Builder,持有VersionSet和当前current_的指针
    将当前的更改edit传入Builder
    将当前Builder应用到Version(当前Version(即current_)+edit=新建Version)
    新建manifest,将当前Version(即current_)和更改(即edit)写入manifest
    旧current_引用减1,将新建Version设置为当前Version
    
    VersionSet log_number,next_file,last_sequence,prev_log_number设置
    数据库启动时会调用Status VersionSet::Recover
    从manifest文件读取所有edit,并更新log_number,next_file,last_sequence,prev_log_number
    如果一开始没有manifest文件,则这些值都为0
    
    versions_->NewFileNumber调用的地方(用于log_file和tablefile):
    Status DBImpl::WriteLevel0Table (用于tablefile,写入0层的sstable)
    Status DBImpl::MakeRoomForWrite(用于log_file)
    Status DB::Open (用于log_file)
    OpenCompactionOutputFile (Compaction时生成新的输出文件tablefile)
    
    如果压缩的时候已生成新的sstable,但还没有写入manifest就断电,
    将会在下次压缩的时候删除这个sstable(因为这个文件没有元数据FileMetaData,从而RemoveObsoleteFiles会删除)
    
    RemoveObsoleteFiles

各组件默认大小: options.h
    memtable: 4MB  size_t write_buffer_size = 4 * 1024 * 1024;  Status DBImpl::MakeRoomForWrite
    sstable:2MB   size_t max_file_size = 2 * 1024 * 1024;        Status DBImpl::DoCompactionWork
    log 文件,由一系列的 32kb 物理块组成  static const int kBlockSize = 32768;   Status Writer::AddRecord
    SStable 的 datablock, 变长,但最小为 4kb   size_t block_size = 4 * 1024;   void TableBuilder::Add
    各 level 的大小 
    level0,不按大小算,而是按照文件个数算,level0 默认为 4 个文件触发一次 compaction。其他还有两个配置,1 个 8 个文件,在 MakeRoomForWrite 函数中使用,一个 12,达到 12 个文件,系统将暂停写入
    // Level-0 compaction is started when we hit this many files.
    static const int kL0_CompactionTrigger = 4;

    // Soft limit on number of level-0 files.  We slow down writes at this point.
    static const int kL0_SlowdownWritesTrigger = 8;

    // Maximum number of level-0 files.  We stop writes    at this point.
    static const int kL0_StopWritesTrigger = 12;
    其他 level, level1 10M, level 2 100M, 逐层递乘 10.   MaxBytesForLevel

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值