版权声明:本文为博主原创文章,未经博主允许不得转载。
数据在插入内存中的时候会在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/ 】