天天看點

【LevelDB源碼剖析系列】SkipList與Memtable

memtable是leveldb的核心資料結構之一,它的底層實作是skiplist,是一個記憶體層面的容器,負載主要的寫需求。

【LevelDB源碼剖析系列】SkipList與Memtable

       skiplist其實一個一個多層次連結清單,在查找時,先在最後層次進行查找,找到比較大的資料時就往低層次退,

【LevelDB源碼剖析系列】SkipList與Memtable

      skiplist有幾個主要底層的函數:

findgreaterorequal

keyisafternode

insert

findlessthan

findlast

randomheight

      實際上skiplist的對外api seek的主要實作就是findgreaterorequal,

      findgreaterorequal包攬了幾乎所有的邏輯,因為在插入和查找時,都需要先進行findgreaterorequal。

      按照多層次的思想就很容易了解findgreaterorequal。

【LevelDB源碼剖析系列】SkipList與Memtable

標明skiplist的最高層(l5)。

從標明層(l5)開始直接跳到>= k43的位置。

以最終結果(紅色方格)的上一跳的元素(橙色方格)開始,退到下一層(l4)再進行跳躍。

重複步驟2直到找到跟k43相等的元素或者已經周遊完l1(最底層)為止。

      從leveldb實作上需要注意findgreaterorequal(const key& key, node** prev)。

      findgreaterorequal在周遊過程中會把圖中橙色方格的指針通通記錄到prev内,供insert步驟修改指針使用。

      了解findgreaterorequal後,insert步驟也迎刃而解。

      初始狀态                                                                                                                                                              

【LevelDB源碼剖析系列】SkipList與Memtable

      findgreaterorequal

【LevelDB源碼剖析系列】SkipList與Memtable

      插入完成

【LevelDB源碼剖析系列】SkipList與Memtable

      insert步驟實際上就是使用findgreaterorequal找到結果以後,再利用結果修改指針,插入資料,具體步驟:

使用randomheight生成新節點的層高(圖中是l6),按照這個層高new一個新node。

如果層高超過原有的最高層高,需要開辟新層次,直接把新層高指向null,如果沒有超過則忽略步驟2。

進行findgreaterorequal。

根據prev傳回的結果,修改prev裡所有的node的指針到新node,同時把新node的指針指向原有node指向的對象。

      memtable主要作用是對skiplist,arena,comparator進行群組合和管理,功能函數上隻是屏蔽了底層操作,對使用者更加優雅,是以無需再次分析。

      其中arena是leveldb自己實作的記憶體池,按一定對齊位元組配置設定,實作簡單,這裡不作具體分析。

【LevelDB源碼剖析系列】SkipList與Memtable

      memtable會對自身做一個refs計數,當refs為零的時候,memtable執行自行清理。

      memtable在leveldb中所起到的作用是提供随機寫的一個容器,相當于一層在leveldb中的外層屏障,肩負最大的寫入壓力。

當memtable增長一定的記憶體時。leveldb會把它轉化為一個immutable_memtable,然後緊接着放到背景進行compaction操作,

使memtable裡面的資料落地。

至此,memtable的主要功能都分析完畢了。