LevelDB源码分析8-db key

本文介绍了LevelDB中Memtable的key结构,包括用于区分数据类型的价值类型(ValueType)、序列号(SequenceNumber),以及用于内部操作的解析内部键(ParsedInternalKey)和内部键(InternalKey)。还详细解释了这些组件如何帮助实现数据更新、删除操作及版本控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了更好的阅读Memtable,先看一看Memtable的key.

ValueType

levledb更新时不会操控db中的数据,每次操作都是直接插入一份kv数据,具体的数据合并和清除工作由后台的compact完成。所以,每次 put,db 中就会新加入一份 KV 数据,即使该 key 已经存在;而 delete 等同于 put 空的 value。为了区分真实 kv 数据和删除操作的 mock 数据,使用 ValueType 来标识:

enum ValueType {
  kTypeDeletion = 0x0, //删除的key
  kTypeValue = 0x1 //插入的key
};

SequenceNumber

SequenceNumber的定义为:typedef uint64_t SequenceNumber;.可见sequenceNumber是一个64位的数。每次更新时,由SequenceNumber来标识,整个 db 有一个全局值保存着当前使用到的 SequnceNumber。SequnceNumber 在 leveldb 有重要的地位,key 的排序,compact 以及 snapshot 都依赖于它。存储结构如下:

056
SequenceNumberValueType

SequnceNumber只占56bits,ValueType占用8bits.

因此定义了其最大值:

//SequenceNumber的最大值,56位
static const SequenceNumber kMaxSequenceNumber =
    ((0x1ull << 56) - 1);

ParsedInternalKey

ParsedInternalKey为db内部操作的key.db内部需要把user key(用户传入的key,为Slice格式)加入元信息一并处理。

//db内部操作的key,对用户传入的key(Slice)加入一个信息
struct ParsedInternalKey {
  Slice user_key;
  SequenceNumber sequence;
  ValueType type;

  ParsedInternalKey() { }  // Intentionally left uninitialized (for speed)
  ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t)
      : user_key(u), sequence(seq), type(t) { }
  std::string DebugString() const;
};

转换函数:

//把一个slice形式的internal_key转为一个ParsedInternalKey的struct
inline bool ParseInternalKey(const Slice& internal_key,
                             ParsedInternalKey* result) {
  const size_t n = internal_key.size();
  if (n < 8) return false;
  uint64_t num = DecodeFixed64(internal_key.data() + n - 8);//seq+type
  unsigned char c = num & 0xff;
  result->sequence = num >> 8;
  result->type = static_cast<ValueType>(c);
  result->user_key = Slice(internal_key.data(), n - 8);//user_key
  return (c <= static_cast<unsigned char>(kTypeValue));
}

 InternalKey

InternalKey为db内部使用,包装易用的结构,包含userkey于SequnceNumber/ValueType

InternalKey是内部成员为一个rep_(string).这个string组成为ParseInternalKey的元素:

//增加一个key的操作
void AppendInternalKey(std::string* result, const ParsedInternalKey& key) {
  result->append(key.user_key.data(), key.user_key.size());//user key
  PutFixed64(result, PackSequenceAndType(key.sequence, key.type));// SequenceNumber 和 ValueType
}

整体的结果如下

//InternalKey
class InternalKey {
 private:
  std::string rep_;//a string represent all the information
 public:
  InternalKey() { }   // Leave rep_ as empty to indicate it is invalid
  //InternalKey string consist of (user_key)
  InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) {
    AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t));
  }

  void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); }
  Slice Encode() const {
    assert(!rep_.empty());
    return rep_;
  }

  Slice user_key() const { return ExtractUserKey(rep_); }

  void SetFrom(const ParsedInternalKey& p) {
    rep_.clear();
    AppendInternalKey(&rep_, p);
  }

  void Clear() { rep_.clear(); }

  std::string DebugString() const;
};

LookupKey

db内部在memetable中查找数据使用

//LookupKey:db内部在memtable中查找数据
class LookupKey {
 public:
  // Initialize *this for looking up user_key at a snapshot with
  // the specified sequence number.
  LookupKey(const Slice& user_key, SequenceNumber sequence);

  ~LookupKey();

  // Return a key suitable for lookup in a MemTable.
  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); }

 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_;
  const char* kstart_;
  const char* end_;
  char space_[200];      // Avoid allocation for short keys

  // No copying allowed
  LookupKey(const LookupKey&);
  void operator=(const LookupKey&);
};

inline LookupKey::~LookupKey() {
  if (start_ != space_) delete[] start_;
}

SkipList中的key

skiplist中的单个节点不仅存储了Key,也存储了value.格式如下图。内部的比较函数使用的是InternalKey。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值