前文:

leveldb 学习记录(一) skiplist

leveldb 学习记录(二) Slice

存储格式:

leveldb数据在内存中以 Memtable存储(核心结构是skiplist 已介绍),当达到一定容量则转换为Immutable Memtable,由后台线程存储进磁盘中.同时另开一个新 Memtable,记录数据.

Memtable记录修改新kv对,可读可写.Immutable Memtable不可更改.

Memtable使用的就是skiplist记录key value

  1. class MemTable {
  2. public:
  3. // MemTables are reference counted. The initial reference count
  4. // is zero and the caller must call Ref() at least once.
  5. explicit MemTable(const InternalKeyComparator& comparator);
  6. //简配版应用计数 初始化时候需要引用ref将计数+1
  7. // Increase reference count.
  8. void Ref() { ++refs_; }
  9.  
  10. // Drop reference count. Delete if no more references exist.
  11. //unref调用减少应用计数.计数为0 则删除自己
  12. void Unref() {
  13. --refs_;
  14. assert(refs_ >= );
  15. if (refs_ <= ) {
  16. delete this;
  17. }
  18. }
  1. //内存使用相关,暂时不关注
  2. size_t ApproximateMemoryUsage();
  3.  
  4. //迭代器 类似MEMTABLE 中元素的指针
  5. Iterator* NewIterator();
  6.  
  7. //KEY是按次序排序,所以结构体内有比较key的定义
  8. struct KeyComparator {
  9. const InternalKeyComparator comparator;
  10. explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { }
  11. int operator()(const char* a, const char* b) const;
  12. };
  13.  
  14. //私有类中包含构造复制函数,达到禁止复制的目的
  15. private:
  16. // No copying allowed
  17. MemTable(const MemTable&);
  18. void operator=(const MemTable&);
    }

Add Get 添加与读取函数, 删除和修改也是添加完成.

修改删除的优化:

实际上的kv删除或者修改,均未删除之前相同的Key记录,只是新增一个修改后的kv对或者带有删除标记的kv对.

因为系统在查找kv对是以由新至旧次序查找,所以肯定是查找到最新的删除或者修改值.

真正的冗余的老KV对在后面compac操作中才是真正的删除(后继介绍)

  1. // Add an entry into memtable that maps key to value at the
  2. // specified sequence number and with the specified type.
  3. // Typically value will be empty if type==kTypeDeletion.
  4. void Add(SequenceNumber seq, ValueType type,
  5. const Slice& key,
  6. const Slice& value);
  7.  
  8. // If memtable contains a value for key, store it in *value and return true.
  9. // If memtable contains a deletion for key, store a NotFound() error
  10. // in *status and return true.
  11. // Else, return false.
  12. bool Get(const LookupKey& key, std::string* value, Status* s);

Add 函数添加 kTypeDeletion类的kv对,表示删除, value内容为空

  1. void MemTable::Add(SequenceNumber s, ValueType type,
  2. const Slice& key,
  3. const Slice& value) {
  4. // Format of an entry is concatenation of:
  5. // key_size : varint32 of internal_key.size()
  6. // key bytes : char[internal_key.size()]
  7. // value_size : varint32 of value.size()
  8. // value bytes : char[value.size()]
  9. // 插入格式为
  10. //|--------|-------------------------|---------------------|
  11. //|key_size|char[internal_key.size()]|value_size|value_size|
  12. //|--------|-------------------------|---------------------|
  13. size_t key_size = key.size();
  14. size_t val_size = value.size();
  15. size_t internal_key_size = key_size + ;
  16. const size_t encoded_len =
  17. VarintLength(internal_key_size) + internal_key_size +
  18. VarintLength(val_size) + val_size;  //最后要插入skiplist的buf的长度
  19. char* buf = arena_.Allocate(encoded_len);
  20. char* p = EncodeVarint32(buf, internal_key_size);  //buf放入internal——key_size 32位
  21. memcpy(p, key.data(), key_size);            //存放指针拷贝实际的key值
  22. p += key_size;                       //指针偏移KEYSIZE字节
  23. EncodeFixed64(p, (s << ) | type);           //存放64位的sequenceNumber 末尾8位空出 最后一位留给数据type
  24. p += ;
  25. p = EncodeVarint32(p, val_size);             //存放实际val内容
  26. memcpy(p, value.data(), val_size);
  27. assert((p + val_size) - buf == encoded_len);
  28. table_.Insert(buf); //skiplist insert
  29. }

Get函数在MemTable中查找key ,查找成功返回TRUE,查找成功但是type为deletion,返回true并且status为NotFound()错误

其他情况返回false

查找有个细节  skiplist返回的是最近的大于或者等于GreaterOrEqual 所以只要关键字相同 不要求序列号sequence

完全一样(序列号肯定是最新的最大的序列号)

然后代码里再次判断

  1. comparator_.comparator.user_comparator()->Compare(
  2. Slice(key_ptr, key_length - 8),
  3. key.user_key()) == 0)

抛开sequence  仅仅比较key是否相等

  1. bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) {
  2. Slice memkey = key.memtable_key();
  3. Table::Iterator iter(&table_);
  4. iter.Seek(memkey.data());
  5. if (iter.Valid()) {
  6. // entry format is:
  7. // klength varint32
  8. // userkey char[klength]
  9. // tag uint64
  10. // vlength varint32
  11. // value char[vlength]
  12. // Check that it belongs to same user key. We do not check the
  13. // sequence number since the Seek() call above should have skipped
  14. // all entries with overly large sequence numbers.
  15. const char* entry = iter.key();
  16. uint32_t key_length;
  17. const char* key_ptr = GetVarint32Ptr(entry, entry+, &key_length);
  18. if (comparator_.comparator.user_comparator()->Compare(
  19. Slice(key_ptr, key_length - ),
  20. key.user_key()) == ) {
  21. // Correct user key
  22. const uint64_t tag = DecodeFixed64(key_ptr + key_length - );
  23. switch (static_cast<ValueType>(tag & 0xff)) {
  24. case kTypeValue: {
  25. Slice v = GetLengthPrefixedSlice(key_ptr + key_length);
  26. value->assign(v.data(), v.size());
  27. return true;
  28. }
  29. case kTypeDeletion:
  30. *s = Status::NotFound(Slice());
  31. return true;
  32. }
  33. }
  34. }
  35. return false;
  36. }

memtable 使用的InternalKey 代码如下

一个字符串的封装和 比较器InternalKeyComparator代码

  1. // Modules in this directory should keep internal keys wrapped inside
  2. // the following class instead of plain strings so that we do not
  3. // incorrectly use string comparisons instead of an InternalKeyComparator.
  4. class InternalKey {
  5. private:
  6. std::string rep_;
  7. public:
  8. InternalKey() { } // Leave rep_ as empty to indicate it is invalid
  9. InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) {
  10. AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t));
  11. }
  12.  
  13. void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); }
  14. Slice Encode() const {
  15. assert(!rep_.empty());
  16. return rep_;
  17. }
  18.  
  19. Slice user_key() const { return ExtractUserKey(rep_); }
  20.  
  21. void SetFrom(const ParsedInternalKey& p) {
  22. rep_.clear();
  23. AppendInternalKey(&rep_, p);
  24. }
  25.  
  26. void Clear() { rep_.clear(); }
  27.  
  28. std::string DebugString() const;
  29. };
  30.  
  31. inline int InternalKeyComparator::Compare(
  32. const InternalKey& a, const InternalKey& b) const {
  33. return Compare(a.Encode(), b.Encode());
  34. }

inline bool ParseInternalKey(const Slice& internal_key,ParsedInternalKey* result) {
  const size_t n = internal_key.size();
  if (n < 8) return false;
  uint64_t num = DecodeFixed64(internal_key.data() + n - 8);
  unsigned char c = num & 0xff; //最后一个字节 代表 类型type
  result->sequence = num >> 8; //左移8位 获取序列号
  result->type = static_cast<ValueType>(c);
  result->user_key = Slice(internal_key.data(), n - 8); //除开信息位的8字节 其余便是数据 转化成 Slice
  return (c <= static_cast<unsigned char>(kTypeValue));
}

  1.  

class LookupKey //DBImpl::Get()查询使用的辅助类
使用两个指针 根据不同需求 提供不同的数据结构

可提供下列三种 Slice

Slice memtable_key()
Slice internal_key()
Slice user_key()

数据都存储在 char space_[200]; // Avoid allocation for short keys

但是如果存储数据过长 则需要重新分配内存

  1. LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) {
  2. size_t usize = user_key.size();
  3. size_t needed = usize + ; // A conservative estimate
  4. char* dst;
  5. if (needed <= sizeof(space_)) {  需要更多的空间 则自行分配和删除
  6. dst = space_;
  7. } else {
  8. dst = new char[needed];
  9. }
  10. start_ = dst;
  11. dst = EncodeVarint32(dst, usize + );
  12. kstart_ = dst;
  13. memcpy(dst, user_key.data(), usize);
  14. dst += usize;
  15. EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek));
  16. dst += ;
  17. end_ = dst;
  18. }
  19. inline LookupKey::~LookupKey() {
  20. if (start_ != space_) delete[] start_;  //自行删除
  21. }

整个类代码如下

  1.  
  1. // A helper class useful for DBImpl::Get()
  2. class LookupKey { //DBImpl::Get()查询使用的辅助类
  3. public:
  4. // Initialize *this for looking up user_key at a snapshot with
  5. // the specified sequence number.
  6. LookupKey(const Slice& user_key, SequenceNumber sequence);
  7.  
  8. ~LookupKey();
  9.  
  10. // Return a key suitable for lookup in a MemTable.
  11. Slice memtable_key() const { return Slice(start_, end_ - start_); }
  12.  
  13. // Return an internal key (suitable for passing to an internal iterator)
  14. Slice internal_key() const { return Slice(kstart_, end_ - kstart_); }
  15.  
  16. // Return the user key
  17. Slice user_key() const { return Slice(kstart_, end_ - kstart_ - ); }
  18.  
  19. private:
  20. // We construct a char array of the form:
  21. // klength varint32 <-- start_
  22. // userkey char[klength] <-- kstart_
  23. // tag uint64
  24. // <-- end_
  25. // The array is a suitable MemTable key.
  26. // The suffix starting with "userkey" can be used as an InternalKey.
  27. const char* start_;
  28. const char* kstart_;
  29. const char* end_;
  30. char space_[]; // Avoid allocation for short keys
  31.  
  32. // No copying allowed
  33. LookupKey(const LookupKey&);
  34. void operator=(const LookupKey&);
  35. };
  36.  
  37. inline LookupKey::~LookupKey() {
  38. if (start_ != space_) delete[] start_;
  39. }
  40.  
  41. }

参考

https://blog.csdn.net/tankles/article/details/7663635

https://blog.csdn.net/sparkliang/article/details/8604424

http://www.cnblogs.com/haippy/archive/2011/12/04/2276064.html

leveldb 学习记录(三) MemTable 与 Immutable Memtable的更多相关文章

  1. leveldb 学习记录(四)Log文件

    前文记录 leveldb 学习记录(一) skiplistleveldb 学习记录(二) Sliceleveldb 学习记录(三) MemTable 与 Immutable Memtablelevel ...

  2. leveldb 学习记录(四) skiplist补与变长数字

    在leveldb 学习记录(一) skiplist 已经将skiplist的插入 查找等操作流程用图示说明 这里在介绍 下skiplist的代码 里面有几个模块 template<typenam ...

  3. JavaScript学习记录三

    title: JavaScript学习记录三 toc: true date: 2018-09-14 23:51:22 --<JavaScript高级程序设计(第2版)>学习笔记 要多查阅M ...

  4. 3.VUE前端框架学习记录三:Vue组件化编码1

    VUE前端框架学习记录三:Vue组件化编码1文字信息没办法描述清楚,主要看编码Demo里面,有附带完整的代码下载地址,有需要的同学到脑图里面自取.脑图地址http://naotu.baidu.com/ ...

  5. leveldb 学习记录(八) compact

    随着运行时间的增加,memtable会慢慢 转化成 sstable. sstable会越来越多 我们就需要进行整合 compact 代码会在写入查询key值 db写入时等多出位置调用MaybeSche ...

  6. leveldb 学习记录(五)SSTable格式介绍

    本节主要记录SSTable的结构 为下一步代码阅读打好基础,考虑到已经有大量优秀博客解析透彻 就不再编写了 这里推荐 https://blog.csdn.net/tankles/article/det ...

  7. webrtc学习———记录三:mediaStreamTrack

    参考: https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack 转自http://c.tieba.baidu.com/p/3 ...

  8. leveldb 学习记录(六)SSTable:Block操作

    block结构示意图 sstable中Block 头文件如下: class Block { public: // Initialize the block with the specified con ...

  9. leveldb 学习记录(七) SSTable构造

    使用TableBuilder构造一个Table struct TableBuilder::Rep { // TableBuilder内部使用的结构,记录当前的一些状态等 Options options ...

随机推荐

  1. Python 输出

    普通的输出 生活中的“输出”: 软件中的“输出”: python中变量的输出: print('hello world') 格式化输出 占位符% print('Hello,%s' % 'Python') ...

  2. Hash 1.04 右键

    http://keir.net/hash.html Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\*\shell\Hash 1.04\ ...

  3. jenkins 可以设置最多执行并发执行多少个

    系统-系统配置

  4. 错误 CS0006 Metadata file 'E:\项目名称\xxxx.dll'

    错误 CS0006 Metadata file 'E:\桌面临时文件\Pos\xxxx.dll' 1.找到这个类库在当前类库右键发生 找到 应用程序-->把程序集名称改成提示错误 的名称 2.找 ...

  5. arcgis10.2 打开CAD文件注记乱码

    1.使用ARCGIS10.2打开CAD文件,图面显示的注记内容为乱码,属性表中的注记内容正常2.同样的CAD文件在ARCGIS9.3中打开正常出现此情况影响历史数据使用,请求ESRI技术支持注:系统添 ...

  6. [Oracle,2018-02-07] Oracle 报错:“ORA-02292:违反完整约束条件(XXX.FKXXX)

    报错的原因很清楚,就是你要删除的记录是另外某条记录的外键,解决办法: 1.删除子记录,在删除本记录: 2.暂时禁用此外键(适合在测试后清除所有记录的情况,记得最后要恢复此外键) 解决办法有了,现在最主 ...

  7. cookies的常见方式

    cookie有如下特点 保存在客户端,一般由浏览器负责存储在本地. 通常是加密存储的,不过由于存储在本地,很难保证数据不被非法访问,并不怎么安全,所以cookies中不宜保存敏感信息,如密码等. 哪些 ...

  8. Java 虚拟机面试题全面解析(干货)

    Java 虚拟机面试题全面解析(干货) JDK 是什么 JRE 是什么 Java历史版本的特性 Java Version SE 50 Java Version SE 6 Java Version SE ...

  9. android toolbar效果3

    Title居中,只有一个右边按钮 activity_main.xml: <?xml version="1.0" encoding="utf-8"?> ...

  10. zookeeper 集群部署

    参考: https://www.cnblogs.com/linuxprobe/p/5851699.html