概述
LSM樹(Log Structure Merge Tree,日志結構合並樹)不是樹,而是一系列樹。日志結構,說的是 SSTable(Sorted String Table,有序字符串表),是一種 Append Only 的日志形式的存儲結構。合並樹,說的是逐層合並 SSTable Index(有序字符串表索引),SSTable Index 采用 B-tree 實現。
整體來看,LSM 樹很適合寫入多於讀取的應用場景,其通過將隨機寫轉換成順序寫,提升寫入效率,但同時因為讀取時需要依次訪問 MemTable 和 SSTables,降低了讀取效率。
讀寫過程
(來源:LSM Tree-Based存儲引擎的compaction策略(feat. RocksDB))
(來源:Flink Forward Berlin 2018: Stefan Richter - "Tuning Flink for Robustness and Performance")
如圖,寫入時,會將數據先寫入 WAL(Write Ahead Log,預寫日志)避免數據丟失;然后再寫入 active memtable(活躍內存表),active memtable 滿了之后再轉入 immutable memtable(不可變內存表),之后再刷入磁盤中的 L0 級 SSTable,並隨着數據量的增加依次壓縮(compact)到 L1、L2 直到設定的最終一級的 SSTable。
而讀取時,會依次從 active memtable 到 immutable memtable 再到 block cache 中查找,如果 block cache 中找無,會從磁盤中的 SSTables 查找,然后再加載到 block cache 中,以便下次直接擊中緩存,提升讀取效率。
SSTable 之結構
SSTable 即有序字符串表,其將鍵值依次追加到 SSTable file 末尾,並將鍵在文件中的偏移量(offset)寫到索引文件中。因為 SSTable 是不可變的(immutable),更新鍵值會首先寫入 Memtable,然后刷入 SSTables,但不會修改以往的數據。讀取的順序性保證了新的鍵值會被讀取到而舊的數據不會被觸及;刪除鍵值時也同理,不過刪除時是寫入一條“墓碑”(tombstone)記錄。
(來源:SSTable and Log Structured Storage: LevelDB)
SSTable 的索引采用 B-tree 實現,
壓縮策略
從上面的描述中,我們知道更新和刪除鍵值時,舊的數據並沒有刪除,隨着更新和刪除操作的增加,SSTables 中保留了越來越多的冗余數據。於是,LSM 提供了兩種壓縮策略。此兩種策略可以混合使用,以便實現最佳效率。比如 RocksDB 就使用了 LCS 策略,但在 L0 級使用了 STCS 策略。下面粗略介紹下 STCS 和 LCS。
STCS
STCS,即 Size-Tiered Compaction Strategy,暫譯為「體積階梯式壓縮策略」。如下圖所示,剛開始的 SSTable 體積較小,隨后就像大魚吃小魚一樣,四個較小的 SSTables 合並成一個中型的 SSTable,而四個中型的 SSTables 又被合並成一個較大的 SSTable,配置該策略時需要指定各個體型(small、medium 和 large)的文件體積大小,看起來就像一個階梯。
(來源:Scylla’s Compaction Strategies Series: Space Amplification in Size-Tiered Compaction)
LCS
LCS,Leveled Compaction Strategy,暫譯為「層級式壓縮策略」。如下圖所示,每個 SSTable 的體積大致相同,但限制每個層級的文件數,當某一層級的文件數超過限定值時,會移動並合並到下一層級。
(來源:Scylla’s Compaction Strategies Series: Write Amplification in Leveled Compaction)