有5个key的概念,可能会让人混淆,下面就来一个一个的分析。
User_key;
最简单的key了,就是用户传入的数据
Slice user_key;
ParsedInternalKey
enum ValueType {
kTypeDeletion = 0x0,
kTypeValue = 0x1
};
//我们后面会讲到,我们对key进行delete操作时其实也是插入一条记录,只不过ValueType用delete标识
typedef uint64_t SequenceNumber;
struct ParsedInternalKey {
Slice user_key;
SequenceNumber sequence;
ValueType type;
};
Sequence number是所有基于op log系统的关键数据,它唯一指定了不同操作的时间顺序。
ValueType type代表的是将对key进行的操作类型。
InternalKey
InternalKey是一个复合概念,是有几个部分组合成的一个key,ParsedInternalKey就是对InternalKey分拆后的结果,也就是说InternalKey是由User key + SequenceNumber + ValueType组合而成的
InternalKey的格式为:
| User key (string) | sequence number (7 bytes) | value type (1 byte) |
把user key放到前面的原因是,这样对同一个user key的操作就可以按照sequence number顺序连续存放了,不同的user key是互不相干的,因此把它们的操作放在一起也没有什么意义。
另外用户可以为user key定制比较函数,系统默认是字母序的。
LookupKey & Memtable Key
Memtable的查询接口传入的是LookupKey,它也是由User Key和Sequence Number组合而成的,从其构造函数:LookupKey(const Slice& user_key, SequenceNumber s)中分析出LookupKey的格式为:
| Size (int32变长)| User key (string) | sequence number (7 bytes) | value type (1 byte) |
注意:
- 这里的Size是user key长度+8,也就是整个字符串长度了;
- value type是kValueTypeForSeek,它等于kTypeValue。
- 由于LookupKey的size是变长存储的,因此它使用kstart_记录了user key string的起始地址,否则将不能正确的获取size和user key;
LookupKey导出了三个函数,可以分别从LookupKey得到Internal Key,Memtable Key和User Key,如下:
// Return a key suitable for lookup in a MemTable.
//可以看出memtable_key就是LookupKey的slice形式
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_ - 8); }
其中start_是LookupKey字符串的开始,end_是结束,kstart_是user key字符串的起始地址。
class LookupKey {
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_;//整个LookupKey的起始位置
const char* kstart_;
const char* end_;
char space_[200]; // Avoid allocation for short keys
}
LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) {
size_t usize = user_key.size();//user_key的长度
size_t needed = usize + 13;
// 为什么加13?因为8是SequenceNumber和 value type的长度,5是Size (int32变长)的最大长度
char* dst;
if (needed <= sizeof(space_)) {
dst = space_;
} else {
dst = new char[needed];//超过预留space就得重新设置start_
}
start_ = dst;
dst = EncodeVarint32(dst, usize + 8);//变长编码size
kstart_ = dst;
memcpy(dst, user_key.data(), usize);
dst += usize;
EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek));
//定长编码SequenceNumber和 value type
dst += 8;
end_ = dst;
}