ArangoDB-存儲引擎分析

當今很多主流DB都使用了 LSM Tree 的存儲模型,包括 LevelDB,HBase,Google BigTable,Cassandra,InfluxDB 等。

ArangoDB果斷採用了RocksDB做的底層存儲引擎,RocksDB存儲模型使用的是LSM-Tree數據結構,老規矩先上圖。

在這裏插入圖片描述
在這裏插入圖片描述

存儲模型

  • WAL(Write Ahead Log)

    在設計數據庫的時候經常被使用,當插入一條數據時,數據先順序寫入 WAL 文件中,之後插入到內存中的 MemTable 中。這樣就保證了數據的持久化,不會丟失數據,並且都是順序寫,速度很快。當程序掛掉重啓時,可以從 WAL 文件中重新恢復內存中的 MemTable。WAL文件結構如下圖,按照寫入的順序來存儲變長的K-V,按照固定長度來分組存儲(可能一個K-V跨多個分組)的目的是便於讀取。

在這裏插入圖片描述

  • MemTable

    MemTable 對應的就是 WAL 文件,是該文件內容在內存中的存儲結構,通常用 SkipList 來實現。MemTable 提供了 k-v 數據的寫入、刪除以及讀取的操作接口。其內部將 k-v 對按照 key 值有序存儲,這樣方便之後快速序列化到 SSTable 文件中,仍然保持數據的有序性。

  • Immutable Memtable

    顧名思義,Immutable Memtable 就是在內存中只讀的 MemTable,由於內存是有限的,通常我們會設置一個閥值,當 MemTable 佔用的內存達到閥值後就自動轉換爲 Immutable Memtable,Immutable Memtable 和 MemTable 的區別就是它是隻讀的,系統此時會生成新的 MemTable 供寫操作繼續寫入。之所以要使用 Immutable Memtable,就是爲了避免將 MemTable 中的內容序列化到磁盤中時會阻塞寫操作。

在這裏插入圖片描述

(跳錶結構可以簡單理解爲有序支持近似二分查找的時間複雜度爲log2(N)

  • SSTable

    SSTable 就是 MemTable 中的數據在磁盤上的有序存儲,其內部數據是根據 key 從小到大排列的。通常爲了加快查找的速度,需要在 SSTable 中加入數據索引,可以快讀定位到指定的 k-v 數據。

    SSTable 通常採用的分級的結構,例如 LevelDB 中就是如此。MemTable 中的數據達到指定閥值後會在 Level 0 層創建一個新的 SSTable。當某個 Level 下的文件數超過一定值後,就會將這個 Level 下的一個 SSTable 文件和更高一級的 SSTable 文件合併,由於 SSTable 中的 k-v 數據都是有序的,相當於是一個多路歸併排序,所以合併操作相當快速,最終生成一個新的 SSTable 文件,將舊的文件刪除,這樣就完成了一次合併過程。

在這裏插入圖片描述
(上圖爲按照多塊來存儲的結構。每塊的K-V都是有序的,多塊也是有序的)

​ 隨着K-V的寫入,會生成很多的SST文件,這部分文件需要被合併到一起。從而降低打開文件數量,並且移除已經不存在的記錄。通常可以 配置兩種方式,通用合併(下圖左側)與level合併(右側)

在這裏插入圖片描述

  • Compaction

    當數據不斷從 Immutable Memtable 序列化到磁盤上的 SSTable 文件中時,SSTable 文件的數量就不斷增加,而且其中可能有很多更新和刪除操作並不立即對文件進行操作,而只是存儲一個操作記錄,這就造成了整個 LSM Tree 中可能有大量相同 key 值的數據,佔據了磁盤空間。爲了節省磁盤空間佔用,控制 SSTable 文件數量,需要將多個 SSTable 文件進行合併,生成一個新的 SSTable 文件。比如說有 5 個 10 行的 SSTable 文件要合併成 1 個 50 行的 SSTable 文件,但是其中可能有 key 值重複的數據,我們只需要保留其中最新的一條即可,這個時候新生成的 SSTable 可能只有 40 行記錄。

  • 讀取

    LSM Tree 的讀取效率並不高,當需要讀取指定 key 的數據時,先在內存中的 MemTable 和 Immutable MemTable 中查找,如果沒有找到,則繼續從 Level 0 層開始,找不到就從更高層的 SSTable 文件中查找,如果查找失敗,說明整個 LSM Tree 中都不存在這個 key 的數據。如果中間在任何一個地方找到這個 key 的數據,那麼按照這個路徑找到的數據都是最新的。

    在每一層的 SSTable 文件的 key 值範圍是不重複的,所以只需要查找其中一個 SSTable 文件即可確定指定 key 的數據是否存在於這一層中。Level 0 層比較特殊,因爲數據是 Immutable MemTable 直接寫入此層的,所以 Level 0 層的 SSTable 文件的 key 值範圍可能存在重複,查找數據時有可能需要查找多個文件。

  • 優化讀取

    因爲這樣的讀取效率非常差,通常會進行一些優化,例如 LevelDB 中的 Mainfest 文件,這個文件記錄了 SSTable 文件的一些關鍵信息,例如 Level 層數,文件名,最小 key 值,最大 key 值等,這個文件通常不會太大,可以放入內存中,可以幫助快速定位到要查詢的 SSTable 文件,避免頻繁讀取。

小結

LSM Tree 的思想非常實用,將隨機寫轉換爲順序寫來大幅提高寫入操作的性能,但是犧牲了部分讀的性能。總的來說就是通過將大量的隨機寫轉換爲順序寫,從而極大地提升了數據寫入的性能,雖然與此同時犧牲了部分讀的性能。只適合存儲 key 值有序且寫入大於讀取的數據,或者讀取操作通常是 key 值連續的數據。

  • 所有記錄在業務上是有序的,對key的查詢其實會執行類似二分查找。
  • 持久化是通過寫入有序文件來實現的。高性能的寫入是通過先寫入內存結構來保證的(寫滿的內存結構刷到持久化文件)。
  • 提供了level機制對數據做分層,優先查詢最新寫入的level來優化查詢性能。

思考

ArangoDB之所以支持的索引花樣多一些,其中很大的原因是來自於底層數據模型的支持。那麼我們如何基於底層的數據模型,來映射到DB給用戶開放的API層的寫入機制和查詢索引呢:)感興趣的同學可在評論區談談自己的觀點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章