MemTable是LevelDB内存的类对象。用户在插入数据时,LevelDB会先将数据插入到Memtable中,MemTable会将数据插入到SkipList中。
class MemTable {
public:
explicit MemTable(const InternalKeyComparator& comparator);
MemTable(const MemTable&) = delete;
MemTable& operator=(const MemTable&) = delete;
void Ref() { ++refs_; }
// Drop reference count. Delete if no more references exist.
void Unref() {
--refs_;
assert(refs_ >= 0);
if (refs_ <= 0) {
delete this;
}
}
// Returns an estimate of the number of bytes of data in use by this
// data structure. It is safe to call when MemTable is being modified.
size_t ApproximateMemoryUsage();
void Add(SequenceNumber seq, ValueType type, const Slice& key,
const Slice& value);
bool Get(const LookupKey& key, std::string* value, Status* s);
private:
struct KeyComparator {
const InternalKeyComparator comparator;
explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) {}
int operator()(const char* a, const char* b) const;
};
typedef SkipList<const char*, KeyComparator> Table;
~MemTable(); // Private since only Unref() should be used to delete it
KeyComparator comparator_;
int refs_;
Arena arena_;
Table table_;
};
MemTable::MemTable(const InternalKeyComparator& comparator)
: comparator_(comparator), refs_(0), table_(comparator_, &arena_) {}
其成员变量有:
KeyComparator comparator_; // 比较器,用于比较 MemTable Entry
// 该比较其内部维护一个InternalKeyComparator,实际比较InternalKey
int refs_; // 计数器,即有多少线程该访问该MemTable
Arena arena_; // 内存申请器,在类内进初始化,然后在创建SkipList时,传过去
Table table_; // MemTable对象内,维护的SkipList
MemTable 实现过程中,有一个关于内存申请有一个细节,注意成员数据 arena_, 在MemTable初始化对象时,arena_ 是在类内初始化。在 MemTable 构造函数中,初始化成员数据 table_ 时,传入了 &arena_。也就是说 MemTable 和其底层结构 SkipList 共用一个内存申请器。
另外一个在LevelDB代码阅读,容易让人混乱的是 key 在各个环节中的存在形式。
在讲解MemTable对外接口之前,先了解两个重要结构。让我们仔细想想,作为用户插入的是一个key-value值,那么数据在 MemTable 中如何表示?SkipList 作为 Memtable 的底层存储结构,数据在SkipList中又如何表示呢?
InternalKey
MemTable不会对已经存储的数据进行delete操作,那么就需要一个sequence_id表示key的先后顺序,并且单独有一个字段标志该key是否已经删除,所以相当于对原始用户输入的key进行扩展,即 InternalKey 结构。
void AppendInternalKey(std::string* result, const ParsedInternalKey& key) {
result->append(key.user_key.data(), key.user_key.size());
PutFixed64(result, PackSequenceAndType(key.sequence, key.type));
}
struct ParsedInternalKey {
Slice user_key;
SequenceNumber sequence;
ValueType type;
};
从接口 AppendInternalKey中,可以清晰看到 InternalKey 是如何构造的。其中Sequence就是全局维护的id,表示key先后顺序。type表示该key是否为删除状态:0(删除)或1。
LookupKey
结构 LookupKey,又称 memtable_key。细心的同学已经看出,该结构体即为 MemTable::Get 接口的输入。LookupKey InternalKey 又进行了扩展,加入 InternalKey 的长度信息。 LookupKey 是 key 的终极形态。
MemTable_entry
MemTable_entry 是用户数据在 SkipList 中的结构。
MemTable::Add & MemTable::Get接口
MemTable::Add
/**
Memtable 插入数据
**/
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()]
size_t key_size = key.size(); // key 字节个数
size_t val_size = value.size(); // value 字节个数
size_t internal_key_size = key_size + 8; // internal_key_size 字节个数
// MemTable_entry 所占字节
const size_t encoded_len = VarintLength(internal_key_size) +
internal_key_size + VarintLength(val_size) +
val_size;
char* buf = arena_.Allocate(encoded_len); // 申请内存
char* p = EncodeVarint32(buf, internal_key_size);
// 填充 MemTable_entry 的内容
std::memcpy(p, key.data(), key_size);
p += key_size;
EncodeFixed64(p, (s << 8) | type);
p += 8;
p = EncodeVarint32(p, val_size);
std::memcpy(p, value.data(), val_size);
assert(p + val_size == buf + encoded_len);
table_.Insert(buf); // 调用 SkipList 的Insert 函数
}
接口 Add 内容其实很好理解。唯一一个疑问是:为什么申请申请内存时,没有进行内存对齐。
MemTable::Get
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: entry 实则就是 MemTable_entry,数据在SkipList中的形式
// klength varint32
// userkey char[klength]
// tag uint64
// vlength varint32
// value char[vlength]
const char* entry = iter.key();
uint32_t key_length;
// key_ptr 指向key的起始位置,key_length大小为 = key.size() + 8,即internal_key_size;
const char* key_ptr = GetVarint32Ptr(entry, entry + 5, &key_length);
// 调用 user key 比较器
if (comparator_.comparator.user_comparator()->Compare(
Slice(key_ptr, key_length - 8), key.user_key()) == 0) {
// key type
const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);
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;
}