Leveldb源码解析第六篇【memtable】

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

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

搞懂Memtable需要阅读如下源码

db/MemTable.h
db/MemTable.cc
db/dbformat.h
db/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,是一个模板类,传入的比较器是InternalKeyComparator
MemTable::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/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值