levelDB数据库中,其中有两部分在内存中保存,memtable和immutable结构。两者使用的其实都是基于类MemTable申请的内存。下面就详细介绍一下Memtable类。
MemTable 和 SkipList 之间有这千丝万缕的联系,MemTable实则是对SkipList进行了封装。
SkipList的声明是一个类模板,他的实例化是在MemTable类声明中进行的,LevelDB中memtable块的底层实现是由SkipList完成的。
SkipList和MemTable都有其对应的迭代器。MemTable迭代器实则是对SkipList::Iterator的进一步封装。
基础函数
const char* GetVarint32Ptr(const char* p, const char* limit, uint32_t* v)
解析levelDB 中Varint32 类型的数据,在levelDB中数据一般先存储数据大小,然后存储真实的数据。
p :Varint32数据的起始地址;
limit : Varint32数据最多用5位,所以limit 为p+5;
v :数据的长度;
return 返回真实数据的起始地址
GetLengthPrefixedSlice函数
hh
static Slice GetLengthPrefixedSlice(const char* data) {
uint32_t len;
const char* p = data;
p = GetVarint32Ptr(p, p + 5, &len); // +5: we assume "p" is not corrupted
return Slice(p, len);
}
EncodeFixed64函数
将uint64类型按照char类型进行存储
inline void EncodeFixed64(char* dst, uint64_t value) {
uint8_t* const buffer = reinterpret_cast<uint8_t*>(dst);
// Recent clang and gcc optimize this to a single mov / str instruction.
buffer[0] = static_cast<uint8_t>(value);
buffer[1] = static_cast<uint8_t>(value >> 8);
buffer[2] = static_cast<uint8_t>(value >> 16);
buffer[3] = static_cast<uint8_t>(value >> 24);
buffer[4] = static_cast<uint8_t>(value >> 32);
buffer[5] = static_cast<uint8_t>(value >> 40);
buffer[6] = static_cast<uint8_t>(value >> 48);
buffer[7] = static_cast<uint8_t>(value >> 56);
}
SkipList 结构
SkipList中每个node节点中key的结构如下,如下结构按照Slice类型存储到node中。
其中 :
internal_key_size 是levelDB内部Varint32类型,记录key.size()+8
value size 也是varint32类型,记录value.size()
MemTable
MemTable类是对SkipList类的封装,那么为什么封装呢?
- SkipList是一个类模板,是没有具体类型的。在MemTable类中,进行具体实例话,存储成成员对象;
- 在append数据的时候,输入数据是不符合SkipList 中节点结构的。需要用输入数据构造出node节点所存储的key结构。构造的过程就放在MemTable中。
- 在MemTable中构造比较器
- …(再思考)
class MemTable {
public:
explicit MemTable(const InternalKeyComparator& comparator);
MemTable(const MemTable&) = delete;
MemTable& operator=(const MemTable&) = delete;
// Increase reference count.
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();
Iterator* NewIterator();
// 构造SkipList的Node节点的key内容,然后调用SkipList::Insert函数,实现添加操作
void Add(SequenceNumber seq, ValueType type, const Slice& key,
const Slice& value);
bool Get(const LookupKey& key, std::string* value, Status* s);
private:
friend class MemTableIterator;
friend class MemTableBackwardIterator;
// MemTable内部维护一个比较器,可以直接比较SkipList结构中node节点
struct KeyComparator {
const InternalKeyComparator comparator; // InternalKey 的比较器
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_;
// MemTable类对SkipList进行封装,其实DB的memtable中的记录都存储在SkipList中。
Table table_;
};
MemTable中有两个友元类:
friend class MemTableIterator;
friend class MemTableBackwardIterator;
其中MemTableIterator是MemTable的迭代器,那为什么将MemTableIterator 定义为MemTable的友元:
在MemTableIterator类中,定义了成员变量MemTable::Table::Iterator iter_, 其中 Table 是类型别名,定义在了MemTable的private中,所以需要将MemTableIterator定义为其友元类,才能访问。
其实MemTableIterator 是对SkipList的iterator的封装类,为了与MemTable类保持一致。
另外,在MemTable类中,最终要的两个成员函数就是:Add()和Get(),一个是向memtable中添加key-value,一个是获取指定key对应的最新value。
Add函数:
步骤:
- 构造插入到skiplist中的node节点
1.1 计算node节点的内存大小
1.2 申请内存
1.3 逐步将 internal_key_size key value_size value 插入到新申请的内存中- 调用skip list的insert接口插入node节点
MemTable::Get函数
获取如下结构的 key的值,结构为size + 对应的key。
bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) {
Slice memkey = key.memtable_key();
// 根据MemTable内部维护的SkipList,创建SkipList的迭代器
Table::Iterator iter(&table_);
// 找到第一个 >= memkey的节点
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;
// GetVarint32Ptr结果:
// key_ptr 指向key的起始位置,key_length大小为 = key.size() + 8,即internal_key_size;
const char* key_ptr = GetVarint32Ptr(entry, entry + 5, &key_length);
if (comparator_.comparator.user_comparator()->Compare(
Slice(key_ptr, key_length - 8), key.user_key()) == 0) {
// Correct user key
const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);
switch (static_cast<ValueType>(tag & 0xff)) {
case kTypeValue: {
// 获取value
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;
}