leveldb memtable
memtable常驻于内存,需要按照key进行排序,通常意义上的话,可以使用二叉查找树来实现,跟进一步可以使用红黑树保证树的平衡,但是leveldb中使用了另外的一种数据结构:跳表Skip List。
memtable声明在db/memtable.h中,定义如下:
- class MemTable
- {
- public:
- // MemTables are reference counted. The initial reference count
- // is zero and the caller must call Ref() at least once.
- explicit MemTable(const InternalKeyComparator& comparator);
- // Increase reference count.
- void Ref()
- {
- ++refs_;
- }
- // Drop reference count. Delete if no more references exist.
- void Unref()
- {
- --refs_;
- assert(refs_ >= 0);
- if (refs_ <= 0) // 如果引用数为0,删除该对象
- {
- delete this;
- }
- }
- // Returns an estimate of the number of bytes of data in use by this
- // data structure.
- //
- // REQUIRES: external synchronization to prevent simultaneous
- // operations on the same MemTable.
- // 返回使用的内存量
- size_t ApproximateMemoryUsage();
- // Return an iterator that yields the contents of the memtable.
- //
- // The caller must ensure that the underlying MemTable remains live
- // while the returned iterator is live. The keys returned by this
- // iterator are internal keys encoded by AppendInternalKey in the
- // db/format.{h,cc} module.
- // 返回迭代器,遍历该memtable
- Iterator* NewIterator();
- // Add an entry into memtable that maps key to value at the
- // specified sequence number and with the specified type.
- // Typically value will be empty if type==kTypeDeletion.
- // 由于采用了LSM结构,所以没有数据删除,只有数据的添加,如果type=
- // kTypeDeletion,这时value为空
- // sequence number:递增的序列,用于数据的恢复
- void Add(SequenceNumber seq, ValueType type,
- const Slice& key,
- const Slice& value);
- // If memtable contains a value for key, store it in *value and return true.
- // If memtable contains a deletion for key, store a NotFound() error
- // in *status and return true.
- // Else, return false.
- // 查询该memtable
- bool Get(const LookupKey& key, std::string* value, Status* s);
- private:
- // 私有化析构函数,这样保证只有Unref()方法能够删除该对象
- ~MemTable(); // Private since only Unref() should be used to delete it
- struct KeyComparator
- {
- const InternalKeyComparator comparator;
- explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { }
- int operator()(const char* a, const char* b) const;
- };
- // 迭代器
- friend class MemTableIterator;
- friend class MemTableBackwardIterator;
- // 跳表类型?作用
- typedef SkipList<const char*, KeyComparator> Table;
- // key比较器
- KeyComparator comparator_;
- // 对象被引用的数量,如果该值为0,则删除该对象
- int refs_;
- // 内存区域的封装
- Arena arena_;
- // 使用跳表数据结构保证内存数据按照key排序
- Table table_;
- // No copying allowed
- MemTable(const MemTable&);
- void operator=(const MemTable&);
- };
这里面有几个注意点,首先Arena对象实现了一套leveldb的内存管理策略,将在下面的文章中进行分析,这里仅仅将其想象成一个内存分配器即可;另外就是Table类型(实际上就是SkipList类型)也将在下面的文章中进行分析。下面主要关注memtable初始化、插入key、查询key,由于LSM模型中memtable中没有数据的“实际”删除,这里并没有实现删除方法。
初始化函数定义如下:
- MemTable::MemTable(const InternalKeyComparator& cmp)
- : comparator_(cmp),
- refs_(0),
- table_(comparator_, &arena_)
- {
- }
就是简单的完成refs_和table_的初始化。插入的函数定义如下:
- 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()]
- // 首先格式化kv数据,之后分别写入,格式如下:
- // key_size, key_bytes, sequence_number|type(固定64位),
- // value_size,value
- size_t key_size = key.size();
- size_t val_size = value.size();
- size_t internal_key_size = key_size + 8;
- 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);
- memcpy(p, key.data(), key_size);
- p += key_size;
- EncodeFixed64(p, (s << 8) | type); // (sequencenum << 8) | type
- p += 8;
- p = EncodeVarint32(p, val_size);
- memcpy(p, value.data(), val_size);
- assert((p + val_size) - buf == encoded_len);
- // 插入数据
- table_.Insert(buf);
- }
思路上相对比较简单,首先对用户传递进来的kv进行格式化,之后调用table_的Insert方法插入到数据库中,需要注意:1. 新出现了Slice类型,基本上和std::string类型向类似,代码比较简单,这里略过;2. 用户传递进来的kv最终被封装到了一个char数组中,格式为key_size, key_bytes, (sequence_number << 8)|type,value_size, value_bytes(其中并没有,这里仅仅是为了区分)。
查询的操作代码如下:
- bool MemTable::Get(const LookupKey& key, std::string* value, Status* s)
- {
- Slice memkey = key.memtable_key();
- Table::Iterator iter(&table_);
- // 查找key
- 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;
- 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) // memtable找到的key和用户查找的key相同
- {
- // Correct user key
- const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);
- switch (static_cast<ValueType>(tag & 0xff)) // 屏蔽低8位
- {
- case kTypeValue: // 如果是kTypeValue类型,表明找到
- {
- Slice v = GetLengthPrefixedSlice(key_ptr + key_length);
- value->assign(v.data(), v.size());
- return true;
- }
- case kTypeDeletion: // 如果是kTypeDeletion删除的数据
- *s = Status::NotFound(Slice());
- return true;
- }
- }
- }
- return false;
- }
Get函数内首先通过Table查找key,如果找到该key,解析key的内容,如果是kTypeValue类型的,返回给客户端查找到的value,如果是kTypeDeletion类型的(参考leveldb源代码分析:理论基础),返回给客户端表明没有找到。这里需要注意的是Get的参数中使用了LookupKey,该类型实际上就是在用户输入的key/value和leveldb内部使用key的起到桥梁的作用,定义如下:
- // A helper class useful for DBImpl::Get()
- 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&);
- };
至此基本上memtable的初始化、读取、插入的操作分析完了,未解决问题如下:
1. SkipList数据结构
2. Arena内存分配策略
leveldb memtable的更多相关文章
- LevelDB源码分析-MemTable
MemTable(db/memtable.h db/memtable.cc db/skiplist.h) LevelDB中存储在内存中的那部分KV数据都存储在memtable中,而memtable中的 ...
- leveldb 学习记录(三) MemTable 与 Immutable Memtable
前文: leveldb 学习记录(一) skiplist leveldb 学习记录(二) Slice 存储格式: leveldb数据在内存中以 Memtable存储(核心结构是skiplist 已介绍 ...
- LevelDb日知录之五:MemTable详解
[LevelDb日知录之五:MemTable详解] LevelDb日知录前述小节大致讲述了磁盘文件相关的重要静态结构,本小节讲述内存中的数据结构Memtable,Memtable在整个体系中的重要地位 ...
- LevelDB学习笔记 (3): 长文解析memtable、跳表和内存池Arena
LevelDB学习笔记 (3): 长文解析memtable.跳表和内存池Arena 1. MemTable的基本信息 我们前面说过leveldb的所有数据都会先写入memtable中,在leveldb ...
- leveldb源码分析--SSTable之Compaction
对于compaction是leveldb中体量最大的一部分,也应该是最为复杂的部分,为了便于理解我们首先从一些基本的概念开始.下面是一些从doc/impl.html中翻译和整理的内容: Level 0 ...
- leveldb 学习笔记之VarInt
在leveldb在查找比较时的key里面保存key长度用的是VarInt,何为VarInt呢,就是变长的整数,每7bit代表一个数,第8bit代表是否还有下一个字节, 1. 比如小于128(一个字节以 ...
- LevelDB库简介
LevelDB库简介 一.LevelDB入门 LevelDB是Google开源的持久化KV单机数据库,具有很高的随机写,顺序读/写性能,但是随机读的性能很一般,也就是说,LevelDB很适合应用在查询 ...
- leveldb - log格式
log文件在LevelDb中的主要作用是系统故障恢复时,能够保证不会丢失数据.因为在将记录写入内存的Memtable之前,会先写入Log文件,这样即使系统发生故障,Memtable中的数据没有来得及D ...
- leveldb - 并发写入处理
在并发写入的时候,leveldb巧妙地利用一个时间窗口做batch写入,这部分代码值得一读: Status DBImpl::Write(const WriteOptions& options, ...
随机推荐
- 斯托克斯公式(Stokes' theorem)
参考:http://spaces.ac.cn/archives/4062/ 参考:https://en.wikipedia.org/wiki/Exterior_derivative 比如Ω是一个曲面( ...
- kubernetes之三 使用kubectl在k8s上部署应用
在上一篇中,我们学习了使用minikube来搭建k8s集群.k8s集群启动后,就可以在上面部署应用了.本篇,我们就来学习如何使用kubectl在k8s上部署应用. 学习之前,可以先从下面这篇博客上了解 ...
- SSM整合中错误:Data truncation: Data too long for column 'gender' at row 1
错误描述 ### SQL: insert into t_customer(name,gender,phone,address) values (?,?,?,?) ### Cause: com.mysq ...
- vscode-函数注释插件-正则插件
1.安装插件KoroFileHeader(函数标准注释) 2.设置 在vscode左下角点击设置按钮,选择“设置”,然后输入“fileheader”, 文件头部注释:Fileheader:custom ...
- eclipse安装心得和环境变量配置的体会
从昨天开始就开始安装eclipse,一开始觉得安装eclipse很简单,肯定就跟下个游戏差不多,但是打开官网之后发现下载的安装包不能用,经过搜索之后发现是因为下载源不对.改过下载源之后下载的安装包竟然 ...
- OpenGL立方体
直接画 #include <windows.h> #include <GL/glut.h> #include <stdio.h> #include <stri ...
- Java面试之基础篇(4)
31.String s = new String("xyz");创建了几个StringObject?是否可以继承String类? 两个或一个都有可能,”xyz”对应一个对象,这个对 ...
- vue props父组件与子组件传值方法
/~~父组件 runshow.vue~~/ <template> <div> <conditions :fenxiConditonsList="propCond ...
- 代理修饰词weak/assign/strong的区别
基于项目报错: WebViewJavascriptBridgeBase 中定义:@property (assign) id <WebViewJavascriptBridgeBaseDelegat ...
- Warning: Failed prop type: Invalid prop `value` supplied to `Picker`.报错问题
在使用antd的日期插件时,不留意就会报各种错误. 例如:Warning: Failed prop type: Invalid prop `value` supplied to `Picker`. 这 ...