MemTable
MemTable
是基于 跳表这种数据结构的一种内存的数据组织形式,之前我们实现的跳表中Node节点是只有一个Key
的,但是我们需要存储的是Key-Value
形式的结构,因此我们就有必要将key/value
封装成为一个Key
。
Internal Key Size
: 顾名思义,Internal Key的大小(通过Varint32进行编码)Internal Key
:Userkey
,SeqNumber
,ValueType(delete or value)
Value Size
: 同样通过Varint32
进行编码
比较的规则
InternalKeyComparator
的Compare
按照下面的优先级进行排序
- 按照
UserKey
进行升序排序- 按照
SequenceNumber
进行降序排序- 按照
ValueType
进行降序排序(不用了,上面两个就足够了)
保证:如果
UserKey
是相同的,那么最新版本会在前面,也就是说前面的到的永远是最新版本的
函数Add
按照上面的格式进行封装到buf中然后插入到
跳表
中
void Memtable::Add(SequenceNumber seq, ValueType type, const Slice& userkey, const Slice& value) {
/*
KeySize: 32bit
KeyByte: char[keysize]
Tag : uint64((seq << 8) | type)
v-size : 32bit
v-Byte : char[valuesize]
*/
size_t key_size = userkey.size();
size_t value_size = value.size();
size_t internal_key_size = key_size + 8; //user key size + 8 byte(seq, valueType)
//值进行很好的压缩,最终需要的存储的空间大小是多少(Byte
const size_t encoded_len = VarintLength(internal_key_size) + internal_key_size +
VarintLength(value_size) + value_size;
char* buf = arena_.Allocate(encoded_len);
//1. internal size
char* p = EncodeVarint32(buf, internal_key_size);
//2. Userkey value
std::memcpy(p, userkey.data(), key_size);
p += key_size;
//3. Seqnumber & type [固定8字节,所以是64-bit]
EncodeFixed64(p, (seq << 8) | type);
p += 8; //fixed 8 bytes
//4. Value size
p = EncodeVarint32(p, value_size);
//5. Value
std::memcpy(p, value.data(), value_size);
//进行断言检测
assert(p + value_size == buf + encoded_len);
table_.Insert(buf); //调用SkipList的插入
}
LookUpKey
我们在使用
Get
函数的时候, 传入的参数需要指定Key
,因此使用LookUpkey类作为一个辅助的类进行一些字段的封装
LookupKey::LookupKey(const Slice& user_key, SequenceNumber sequence) {
size_t usize = user_key.size();
size_t needed = usize + 15; //保守的估计,实际上一般用不了这么多
char* dst;
if (needed <= sizeof(space_)) {
dst = space_;
} else {
dst = new char[needed];
}
//1. keysize_
size_ = dst; //size_是开始的位置
dst = EncodeVarint32(dst, usize + 8); //usersize + 8byte
//2. userkey
userKey_ = dst; //下面接上size_之后的位置
std::memcpy(dst, user_key.data(), usize); //将userkey写过来
//3. 8-byte (seq + type)
dst += usize;
EncodeFixed64(dst, PackSeqAndType(sequence, kValueTypeForSeek)); //将seq & type(valueType)一起写到dst中
dst += 8;
end_ = dst; //the end of the dst
}
Get函数
Get函数,从跳表中拿出数据传入的参数通过
lookupkey
进行封装。其中调用了很多的解压缩的函数,压缩的方式是按照leveldb
中coding
的声明来实现的。
bool Memtable::Get(const LookupKey& key, std::string* value, Status* status) {
Slice memkey = key.memtable_key();
Table::Iterator iter(&table_);
iter.Seek(memkey.data()); //寻找的是key的部分,最终查找的结果存放在iter类中的node_中
if (iter.Valid()) {
const char* entry = iter.key(); //从跳表中拿到的entry
uint32_t key_length = 0;
//这个函数会拿到internal_size, 并且跳过size的部分
const char* userkey_ptr = GetVarint32Ptr(entry, entry + 5, &key_length);
if (comparator_.comparator.user_Comparator()->Compare(key.user_key(), Slice(userkey_ptr, key_length - 8)) == 0) {
//传入的userKey同跳表中拿到的userKey是一样的
//sequence & type
const uint64_t tag = DecodeFixed64(userkey_ptr + key_length);
switch (static_cast<ValueType>(tag & 0xff)) {
case kTypeValue: {
Slice v = GetLengthPrefixedSlice(userkey_ptr + key_length);
value->assign(v.data(), v.size());
return true;
}
case kTypeDelete:
*status = Status::NotFound(Slice());
return true;
}
}
}
return false;
}