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; } }


 

 

 欢迎参与评论,请不吝赐教~~

 

  

posted @ 2016-05-07 17:34  _in_the_way  阅读(1629)  评论(0)    收藏  举报