LotusDB 是一個全新的 KV 儲存引擎,Github 地址:https://github.com/flower-corp/lotusdb,希望大家多多支援呀,點個 star 或者參與進來!
LotusDB 是一個基於 LSM Tree 進行設計,並結合 B+ 樹優勢的單機 KV 儲存引擎,讀寫效能穩定、快速。
在傳統的 LSM Tree 架構中,增刪資料均是追加有序寫入到 SST 檔案中,相同的 key 對應的資料可能存在多份,需要通過複雜的 compaction 策略來進行空間回收,這同時帶來了空間放大和寫放大問題。
LSM Tree 在磁碟上維護多級 SSTable 檔案,在資料讀取時,需要逐層掃描檔案來查詢指定的資料,最壞情況下需要掃描每一層的 SSTable,讀效能不穩定。
和 LSM Tree 相對應的,另一種常見的資料儲存模型是 B+ Tree,B+ 樹由於有著很好的適配磁碟頁的特性,在資料庫儲存引擎中廣泛應用,例如最為人熟知的 Mysql 的 InnoDB 引擎。
B+ Tree 將資料維護在樹最底層葉子節點中,讀效能比較穩定,但是資料的插入和更新均是隨機 IO 進行寫入,導致 B+ Tree 的寫效能相對較低。
我們知道,LSM 儲存模型誕生於 HDD(機械硬碟) 時代,HDD 的隨機和順序讀寫速度差別巨大,所以 LSM 的設計最大限度的發揮了順序 IO 的優勢,所有的資料先到記憶體 buffer 裡快取,然後批量有序寫入到檔案中。但是隨著儲存硬體的更新迭代,磁碟的隨機和順序讀寫差別變小了,在一些介質中,順序和隨機讀寫甚至沒有太大的差別。
LSM Tree 針對順序 IO 的一些設計就會顯得過於複雜,導致整個系統難以實現和控制(如果你熟悉 rocksdb 的話,就會深有體會)。
自行設計一個系統的底層儲存引擎,比掌握一個複雜的專案要更加容易,出現了相關的問題也更容易定位和解決,這也是為什麼 cockroach 採用自研的 Pebble 儲存引擎替代 rocksdb,而 LotusDB 就是一個這樣可以輕易學習和掌握的儲存引擎,因為它簡潔、直觀且高效。
LotusDB 的整體架構圖如下:
LotusDB 仍然保留了 LSM Tree 中的寫流程,因為這能夠最大限度的保證寫入資料的永續性以及寫吞吐,所以在磁碟上維護了 WAL 日誌,新寫入的資料先追加到 WAL 中保證資料不丟失,然後再寫入到記憶體中。
記憶體中維護了多個跳錶結構,最新的跳錶叫做 active memtable,一個 memtable 寫滿之後,會變為 immutable memtable,即不可變的 memtable,其不能接收新的寫入,並且等待被後臺執行緒 flush 到磁碟中。
Flush 的時候,資料索引資訊會被存放到 B+ 樹中,而 value 會被單獨存放到 Value Log 中,value log 的結構類似於 WAL,資料寫入都是採用日誌追加,只不過 value log 會有一個閾值,寫滿之後會開啟一個新的 value log,因此 value log 是存在多個的。
需要注意的是,B+ Tree 應該儘量儲存新的儲存介質中,例如固態硬碟,因為前面提到過 B+ 樹是隨機寫入,如果使用傳統機械硬碟的話,寫效能受限制,寫放大嚴重,Flush 可能會是一個瓶頸。
這就是 LotusDB 的整體實現,在這種實現下,我們來看看基本的資料讀寫流程是什麼樣的。
寫一個 key/value:前面說過了,和 LSM 模型完全一致,先將 key/value 封裝成一條日誌追加到 WAL 中,然後將 k/v 寫入到記憶體的 active memtable。
根據 key 讀一個 value:先在記憶體當中的 active memtable 和 immutable memtable 中依次查詢,如果找到直接返回。否則說明 value 可能在磁碟中,就從 B+ 樹獲取 key 的索引資訊,索引資訊是一個二元組 <fid, offset>,標識 value 位於 value log 中具體哪個檔案,以及檔案中的位置,然後直接根據這個索引資訊到 value log 檔案中獲取 value 即可。
最後再來總結下 LotusDB 架構的優點,簡單歸納大概有如下幾點:
1、寫資料流程和傳統 LSM 模型完全一致,保證了順序 IO 的高吞吐,以及資料永續性
2、讀效能相較於原生 LSM 模型更加穩定,讀放大降低,因為引入了 B+ 樹,得益於 B+ 樹穩定的讀效能,整體的讀取效率會更加可控
3、完全去除了 LSM Tree 模型中的多級 SSTable,沒有了 SSTable 的維護,並且採用已有的 B+ 樹實現(BoltDB),大大降低了系統的複雜性
4、Compaction 對儲存介質的損耗降低,LotusDB 中只有 value log 存在 Compaction;原生 LSM 不僅 SSTable 需要 Compaction,並且如果進行了 kv 分離的話,value log 也同樣需要 Compaction
5、讀寫流程簡潔直觀,沒有 bloom filter、block cache 等
LotusDB Github 地址:https://github.com/flower-corp/lotusdb