前文:

leveldb 学习记录(一) skiplist

leveldb 学习记录(二) Slice

存储格式:

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

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

Memtable使用的就是skiplist记录key value

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

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

修改删除的优化:

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

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

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

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

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

void MemTable::Add(SequenceNumber s, ValueType type,
const Slice& key,
const Slice& value) {
// Format of an entry is concatenation of:
// key_size : varint32 of internal_key.size()
// key bytes : char[internal_key.size()]
// value_size : varint32 of value.size()
// value bytes : char[value.size()]
// 插入格式为
//|--------|-------------------------|---------------------|
//|key_size|char[internal_key.size()]|value_size|value_size|
//|--------|-------------------------|---------------------|
size_t key_size = key.size();
size_t val_size = value.size();
size_t internal_key_size = key_size + ;
const size_t encoded_len =
VarintLength(internal_key_size) + internal_key_size +
VarintLength(val_size) + val_size;  //最后要插入skiplist的buf的长度
char* buf = arena_.Allocate(encoded_len);
char* p = EncodeVarint32(buf, internal_key_size);  //buf放入internal——key_size 32位
memcpy(p, key.data(), key_size);            //存放指针拷贝实际的key值
p += key_size;                       //指针偏移KEYSIZE字节
EncodeFixed64(p, (s << ) | type);           //存放64位的sequenceNumber 末尾8位空出 最后一位留给数据type
p += ;
p = EncodeVarint32(p, val_size);             //存放实际val内容
memcpy(p, value.data(), val_size);
assert((p + val_size) - buf == encoded_len);
table_.Insert(buf); //skiplist insert
}

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

其他情况返回false

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

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

然后代码里再次判断

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

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

bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) {
Slice memkey = key.memtable_key();
Table::Iterator iter(&table_);
iter.Seek(memkey.data());
if (iter.Valid()) {
// entry format is:
// klength varint32
// userkey char[klength]
// tag uint64
// vlength varint32
// value char[vlength]
// Check that it belongs to same user key. We do not check the
// sequence number since the Seek() call above should have skipped
// all entries with overly large sequence numbers.
const char* entry = iter.key();
uint32_t key_length;
const char* key_ptr = GetVarint32Ptr(entry, entry+, &key_length);
if (comparator_.comparator.user_comparator()->Compare(
Slice(key_ptr, key_length - ),
key.user_key()) == ) {
// Correct user key
const uint64_t tag = DecodeFixed64(key_ptr + key_length - );
switch (static_cast<ValueType>(tag & 0xff)) {
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;
}

memtable 使用的InternalKey 代码如下

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

// Modules in this directory should keep internal keys wrapped inside
// the following class instead of plain strings so that we do not
// incorrectly use string comparisons instead of an InternalKeyComparator.
class InternalKey {
private:
std::string rep_;
public:
InternalKey() { } // Leave rep_ as empty to indicate it is invalid
InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) {
AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t));
} void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); }
Slice Encode() const {
assert(!rep_.empty());
return rep_;
} Slice user_key() const { return ExtractUserKey(rep_); } void SetFrom(const ParsedInternalKey& p) {
rep_.clear();
AppendInternalKey(&rep_, p);
} void Clear() { rep_.clear(); } std::string DebugString() const;
}; inline int InternalKeyComparator::Compare(
const InternalKey& a, const InternalKey& b) const {
return Compare(a.Encode(), b.Encode());
}

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));
}

 

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

可提供下列三种 Slice

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

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

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

LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) {
size_t usize = user_key.size();
size_t needed = usize + ; // A conservative estimate
char* dst;
if (needed <= sizeof(space_)) {  需要更多的空间 则自行分配和删除
dst = space_;
} else {
dst = new char[needed];
}
start_ = dst;
dst = EncodeVarint32(dst, usize + );
kstart_ = dst;
memcpy(dst, user_key.data(), usize);
dst += usize;
EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek));
dst += ;
end_ = dst;
}
inline LookupKey::~LookupKey() {
if (start_ != space_) delete[] start_;  //自行删除
}

整个类代码如下


 // A helper class useful for DBImpl::Get()
class LookupKey { //DBImpl::Get()查询使用的辅助类
public:
// Initialize *this for looking up user_key at a snapshot with
// the specified sequence number.
LookupKey(const Slice& user_key, SequenceNumber sequence); ~LookupKey(); // Return a key suitable for lookup in a MemTable.
Slice memtable_key() const { return Slice(start_, end_ - start_); } // Return an internal key (suitable for passing to an internal iterator)
Slice internal_key() const { return Slice(kstart_, end_ - kstart_); } // Return the user key
Slice user_key() const { return Slice(kstart_, end_ - kstart_ - ); } private:
// We construct a char array of the form:
// klength varint32 <-- start_
// userkey char[klength] <-- kstart_
// tag uint64
// <-- end_
// The array is a suitable MemTable key.
// The suffix starting with "userkey" can be used as an InternalKey.
const char* start_;
const char* kstart_;
const char* end_;
char space_[]; // Avoid allocation for short keys // No copying allowed
LookupKey(const LookupKey&);
void operator=(const LookupKey&);
}; inline LookupKey::~LookupKey() {
if (start_ != space_) delete[] start_;
} }

参考

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. 用TreeSet和Comparator给list集合元素去重

    今天在做导入功能时,看到一个感觉很好的去重算法,特分享给大家看看: 其原理利用了以下几点: 1.TreeSet里面不会有重复的元素,所以当把一个List放进TreeSet里面后,会自动去重 2.Tre ...

  2. Android中是否推荐使用枚举Enum

    一.Enum的产生 Java1.5中引入了枚举的语法,包括Enum,EnumSet,EnumMap等.其中Enum就是我们在C或C++中见过的枚举类型,但是Java中的枚举又比C或C++中的枚举更成熟 ...

  3. centos7 源码安装redis

    安装3.x [root@node1 ~]# yum install wget gcc-c++ make [root@node1 ~]# wget http://download.redis.io/re ...

  4. 基础 - 字符读取函数scanf、getchar、gets、cin(清空缓存区解决单字符回车问题)

    0x01 scanf.getchar.cin读取单字符: 如下: //scanf读取字符 回车问题 void Sub_1_1() { char v1,v2; scanf("%c", ...

  5. [UE4]ProgressBar,进度条

    准备好2张进度条图片 一.新建名为“testProgress”的UserWidget,添加一个名为“ProgressBar_0”的ProgressBar到默认容器Canvas Panel 二.进度条进 ...

  6. [sharepoint]修改Item或者File的Author和Editor

    写在前面 最近项目中调用sharepoint rest api方式获取文件或者Item列表,而用的方式是通过证书请求,在上传文件,或者新建item的时候,默认的用户是在sharepoint端注册的用户 ...

  7. [java,2017-05-15] 内存回收 (流程、时间、对象、相关算法)

    内存回收的流程 java的垃圾回收分为三个区域新生代.老年代. 永久代 一个对象实例化时 先去看伊甸园有没有足够的空间:如果有 不进行垃圾回收 ,对象直接在伊甸园存储:如果伊甸园内存已满,会进行一次m ...

  8. PHP安装Commposer

    一先把php加到环境变量里面测试 看一下版本号: 二,composer得安装注意安装的时候 php必须在5.59以上版本,openssl的扩展开启,pdo的扩展开启,mbstring的扩展开启 1,下 ...

  9. subString(index,end) 用法

    sb = sb.Substring(0, sb.Length - 1); 获取当前字符串的前一部分去掉最后一个字符

  10. Linux常用基础操作命令大全(超实用精心整理)

    相信大家都对黑客那种只用命令行对电脑操作的风格惊呆了,其实你也可以做到.linux是一款不同于windows的操作系统,而且它是黑客.渗透人员.运维人员等等必会的.如果你想学习,小编下面整理的命令将会 ...