LevelDb 剖析之三 (内存中的数据结构Memtable)
内存中的数据结构Memtable:
LevelDb前述小节大致讲述了磁盘文件相关的重要静态结构,本小节讲述内存中的数据结构Memtable,Memtable在整个体系中的重要地位也不言而喻。总体而言,所有KV数据都是存储在Memtable,Immutable Memtable和SSTable中的,Immutable Memtable从结构上讲和Memtable是完全一样的,区别仅仅在于其是只读的,不允许写入操作,而Memtable则是允许写入和读取的。当Memtable写入的数据占用内存到达指定数量,则自动转换为Immutable Memtable,等待Dump到磁盘中,系统会自动生成新的Memtable供写操作写入新数据,理解了Memtable,那么Immutable Memtable自然不在话下。
LevelDb的MemTable提供了将KV数据写入,删除以及读取KV记录的操作接口,但是事实上Memtable并不存在真正的删除操作,删除某个Key的Value在Memtable内是作为插入一条记录实施的,但是会打上一个Key的删除标记,真正的删除操作是Lazy的,会在以后的Compaction过程中去掉这个KV。
需要注意的是,LevelDb的Memtable中KV对是根据Key大小有序存储的,在系统插入新的KV时,LevelDb要把这个KV插到合适的位置上以保持这种Key有序性。其实,LevelDb的Memtable类只是一个接口类,真正的操作是通过背后的SkipList来做的,包括插入操作和读取操作等,所以Memtable的核心数据结构是一个SkipList。
SkipList是由William Pugh发明。他在Communications of the ACM June 1990, 33(6) 668-676 发表了Skip lists: a probabilistic alternative to balanced trees,在该论文中详细解释了SkipList的数据结构和插入删除操作。
SkipList是平衡树的一种替代数据结构,但是和红黑树不相同的是,SkipList对于树的平衡的实现是基于一种随机化的算法的,这样也就是说SkipList的插入和删除的工作是比较简单的。
关于SkipList的详细介绍可以参考这篇文章:http://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html,讲述的很清楚,LevelDb的SkipList基本上是一个具体实现,并无特殊之处。
SkipList不仅是维护有序数据的一个简单实现,而且相比较平衡树来说,在插入数据的时候可以避免频繁的树节点调整操作,所以写入效率是很高的,LevelDb整体而言是个高写入系统,SkipList在其中应该也起到了很重要的作用。Redis为了加快插入操作,也使用了SkipList来作为内部实现数据结构.
下面呢是Memtable的核心数据结构 SkipList 的具体的实现:
typedef int KeyType;
typedef char* ValueType;
typedef struct SkipListNode //节点结构
{
KeyType _key;
ValueType _value;
SkipListNode* _forward[1];
}Node;

#define MaxNumberOfLevels 16
#define MaxLevel (MaxNumberOfLevels - 1)
#define NewNodeOfLevel(L) (Node*)malloc(sizeof(Node) + (L)*sizeof(Node*)) //////////
Node* NIL;
class SkipList /*跳表结构*/ { public: SkipList() :_level(0), _head(NewNodeOfLevel(MaxNumberOfLevels)) { for (int i = 0; i < MaxNumberOfLevels; ++i) { _head->_forward[i] = NIL; } } ~SkipList() { Node* cur = _head; while (cur) { cur = cur->_forward[0]; free (_head); _head = cur; } }
}![]()
bool SkipList::_Insert(const KeyType& key, const ValueType& value) /*插入节点*/ { Node* Update[MaxNumberOfLevels]; int Level = _level; Node* cur = _head; Node* next; do /*寻找插入位置,并将其路径保存在 Update 中*/ { while (next = cur->_forward[Level],next != NIL && next->_key < key) { cur = next; } Update[Level] = cur; } while (--Level>=0); if (next != NIL && next->_key == key) { next->_value = value; return false; } else { int NewLevel = _RandLevel(); if (NewLevel > _level && _level < MaxLevel) { Update[++_level] = _head; }
Level = _level; //创造出新的节点 Node* insert = NewNodeOfLevel(Level); insert->_key = key; insert->_value = value; //更新指针域 do { insert->_forward[Level] = Update[Level]->_forward[Level]; Update[Level]->_forward[Level] = insert; } while (--Level >= 0); return true; } }
bool SkipList::_Delete(const KeyType& key) { Node* Update[MaxNumberOfLevels]; int Level = _level; Node* Cur = _head; Node* Next; //查找删除位置 do { while (Next = Cur->_forward[Level], Next != NIL && Next->_key < key) Cur = Cur->_forward[Level]; Update[Level] = Cur; } while (--Level >= 0); Level = _level; //找到了相应的值了,需要删除了 if (Next != NIL && Next->_key == key) { for (int i = 0; i <= Level && Update[i]->_forward[i] == Next; ++i) //修改指针的指向 Update[i]->_forward[i] = Next->_forward[i]; free(Next); while (Level > 0 && _head->_forward[Level] == NIL) //减掉相应的层数 --Level; _level = Level; return true; }
//没找到就返回false else { return false; } }
欢迎参与评论,请不吝赐教~~



浙公网安备 33010602011771号