Leveldb源码解析第六篇【memtable】

来源:互联网 发布:2016qq悄悄话破解软件 编辑:程序博客网 时间:2024/06/05 20:35

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

数据在插入内存中的时候会在key前面和后面添加不同的表示,形成多种分类

搞懂Memtable需要阅读如下源码

db/MemTable.hdb/MemTable.ccdb/dbformat.hdb/dbformat.cc

MemTable.h

class MemTable { public:  // 构造方法,传入的是InternalKey类型的比较器  explicit MemTable(const InternalKeyComparator& comparator);  // 计数器  void Ref() { ++refs_; }  // 如果计数器小于等于0,释放memtable  void Unref() {    --refs_;    assert(refs_ >= 0);    if (refs_ <= 0) {      delete this;    }  }  // 返回memtable占用的空间大小  size_t ApproximateMemoryUsage();  // 迭代器  Iterator* NewIterator();  // 在memtable中添加一个key-value  void Add(SequenceNumber seq, ValueType type,           const Slice& key,           const Slice& value);  bool Get(const LookupKey& key, std::string* value, Status* s); private:  ~MemTable();  // Private since only Unref() should be used to delete it  struct KeyComparator {    const InternalKeyComparator comparator;    explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { }    int operator()(const char* a, const char* b) const;  };  friend class MemTableIterator;  friend class MemTableBackwardIterator;  typedef SkipList<const char*, KeyComparator> Table;   // Table实际是一个skiplist  KeyComparator comparator_;    // key比较器  int refs_;  Arena arena_;                 // 分配内存的  Table table_;                 // 跳跃表  // No copying allowed  MemTable(const MemTable&);  void operator=(const MemTable&);};

memtable里面有一个跳跃表,几个迭代器,几个key比较器还有两个重要的函数add和get,下面来介绍memtable的具体实现

MemTable.cc

// 文件一开始就是这个函数,这个函数是用编码后的字符串中得到我们想要的数据// 分为2个部分,第一个部分是uint32类型,存放的是真正数据的大小;第二个部分就是放的真正的数据了static Slice GetLengthPrefixedSlice(const char* data) {  uint32_t len;  const char* p = data;  // 第一部分是编码后放到data中的,所以先反编码得到具体内容,也就是调用GetVarint32Ptr,len会存放反编码出来结果,p会指向真正数据位置  p = GetVarint32Ptr(p, p + 5, &len);  // +5: we assume "p" is not corrupted  // p指向的是真正数据位置,往后数len个长度就是想得到的数据了  return Slice(p, len);}// 构造方法,table_就是上文中说的skiplist,是一个模板类,传入的比较器是InternalKeyComparatorMemTable::MemTable(const InternalKeyComparator& cmp)    : comparator_(cmp),      refs_(0),      table_(comparator_, &arena_) {}// memtable中内部结构体KeyComparator的operator是用来比较两个编码后的字符串的大小int MemTable::KeyComparator::operator()(const char* aptr, const char* bptr)    const {  // Internal keys are encoded as length-prefixed strings.  Slice a = GetLengthPrefixedSlice(aptr);  Slice b = GetLengthPrefixedSlice(bptr);  return comparator.Compare(a, b);}// 编码key,先将key的size编码后放在前面,然后将key的真正内容放到后面static const char* EncodeKey(std::string* scratch, const Slice& target) {    scratch->clear();    PutVarint32(scratch, target.size());    scratch->append(target.data(), target.size());    return scratch->data();}// 内部迭代器使用的就是跳跃表的内部迭代器class MemTableIterator: public Iterator { public:  explicit MemTableIterator(MemTable::Table* table) : iter_(table) { }  virtual bool Valid() const { return iter_.Valid(); }  virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); }  virtual void SeekToFirst() { iter_.SeekToFirst(); }  virtual void SeekToLast() { iter_.SeekToLast(); }  virtual void Next() { iter_.Next(); }  virtual void Prev() { iter_.Prev(); }  virtual Slice key() const { return GetLengthPrefixedSlice(iter_.key()); }  virtual Slice value() const {    Slice key_slice = GetLengthPrefixedSlice(iter_.key());    return GetLengthPrefixedSlice(key_slice.data() + key_slice.size());  }  virtual Status status() const { return Status::OK(); } private:  MemTable::Table::Iterator iter_;  std::string tmp_;       // For passing to EncodeKey  // No copying allowed  MemTableIterator(const MemTableIterator&);  void operator=(const MemTableIterator&);};// 划重点,往memtable中添加key-value// 同样是将key-value加一些头和尾,然后放到一个字符串buf中,再将字符串放到skiplist中// buf中有放了啥呢,|压缩编码(key_size+8)|key|SequenceNumber|type|value_size|value|void MemTable::Add(SequenceNumber s, ValueType type,                   const Slice& key,                   const Slice& value) {  size_t key_size = key.size();  size_t val_size = value.size();  // internal_key是key加上SequenceNumber和ValueType后组成的新key,SequenceNumber和ValueType会占用8个字节  size_t internal_key_size = key_size + 8;  // key-value编码后的长度,第一段存放internal_key_size,第二段存放internal_key,第三段存放val_size,第四段存放val  const size_t encoded_len =      VarintLength(internal_key_size) + internal_key_size +      VarintLength(val_size) + val_size;  // 给buf分配相应的空间  char* buf = arena_.Allocate(encoded_len);  // 先将internal_key_size放到buf中  char* p = EncodeVarint32(buf, internal_key_size);  再将key放到buf中,使用memcpy函数p是不会移位的,需要手动移位  memcpy(p, key.data(), key_size);  // p往右移动key_size个字节  p += key_size;  // SequenceNumber左移8位,或上type;也就是前7个字节放SequenceNumber,后一个字节放ValueType  EncodeFixed64(p, (s << 8) | type);  // p往右移动8个字节  p += 8;  // 再将val_size放到buf中  p = EncodeVarint32(p, val_size);  // 最后放value  memcpy(p, value.data(), val_size);  assert((p + val_size) - buf == encoded_len);  // 插入到skiplist中  table_.Insert(buf);}// 先说说LookupKey是什么,LookupKey是在dbformat.h中定义的// LookupKey里面有多种不同的key// 在add方法中有将一个key-value编码成这种格式:|压缩编码(key_size+8)|key|SequenceNumber|type|value_size|value|// memtable_key:|压缩编码(key_size+8)|key|SequenceNumber|type|// internal_key: |key|SequenceNumber|type|//     user_key: |key|bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) {  // 先得到memtable_key,请看上面memtable_key解释  Slice memkey = key.memtable_key();  Table::Iterator iter(&table_);  // table_中存的key-value,而我们找的是key,这个要怎么截取呢?  // table_传进去比较器是KeyComparator,KeyComparator会得到传入的两个key的internal_key,然后再比较、  // 就算我们传入的是一个entry,得到的internal_key是不会变的  // seek找到的key是大于或等于memkey的  iter.Seek(memkey.data());  if (iter.Valid()) {    const char* entry = iter.key();    uint32_t key_length;    // 将key的长度放到key_length中,key_ptr指向真正的数据    const char* key_ptr = GetVarint32Ptr(entry, entry+5, &key_length);    // 如果user_key相等,说明找到了这个key,但是还需要知道这个key是不是已经删除了    // 判断时候删除就需要得到key后8个字节的最后一个字节了    if (comparator_.comparator.user_comparator()->Compare(            Slice(key_ptr, key_length - 8),            key.user_key()) == 0) {      // 先得到最后8个字节      const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);      // 与上0xff就可以得到最后一个字节,判断类型      switch (static_cast<ValueType>(tag & 0xff)) {        // 如果没有删除返回true        case kTypeValue: {          Slice v = GetLengthPrefixedSlice(key_ptr + key_length);          value->assign(v.data(), v.size());          return true;        }        // 如果删除,返回将状态码改成没有找到        case kTypeDeletion:          *s = Status::NotFound(Slice());          return true;      }    }  }  return false;}

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

原创粉丝点击