技術分享 | LSM-Tree 和 OceanBase 分層轉儲

ITPUB社群發表於2023-03-10

作者:金長龍

愛可生測試工程師,負責DMP產品的測試工作

本文來源:原創投稿

* 愛可生開源社群出品,原創內容未經授權不得隨意使用,轉載請聯絡小編並註明來源。


先前在做 OB 儲存引擎這塊學習的時候,對 OceanBase 的分層轉儲和 SSTable這塊有些細節就懵懵的,比如L0層的 mini SSTable 的每次生成是否就計入轉儲次數,L0層到L1層轉儲的時機以及和 minor_compact_trigger 之間的關係等。今天就這部分內容做個更細緻的探究,試圖更深入的理解 OceanBase 的分層轉儲。

一、LSM-Tree

首先來看一下 LSM-Tree(全稱是 Log-Structured Merge Tree),當下許多較新的資料庫都會選擇 LSM-Tree 作為儲存結構,比如 TiDB 、Cassandra 、OceanBase 等。LSM-Tree 的優勢是順序寫,提升了整體寫入效能。
技術分享 | LSM-Tree 和 OceanBase 分層轉儲
LSM-Tree 大致可以分為兩部分:
  • Memt
    able
    : 常駐記憶體的 KV 查詢樹  +
    無序的 WAL 檔案
  • SS
    Table (Sorted Strin
    g Table)
    : 一組儲存在磁碟的不可變檔案,儲存有序的鍵值對

寫入流程

1、同步寫 Memtable
先將資料寫入 WAL 檔案,然後修改記憶體中的 AVL,因此最優情況下,每次寫操作只有一次磁碟 I/O。
刪除操作並不會直接刪除磁碟中的內容,而是將刪除標記(tombstone)寫入 Memtable 。當 Memtable 增大到一定程度後,則會轉換為 Immutable Memtable 併產生一個新的 Memtable 接受寫操作。
2、非同步寫 SSTable
後臺會啟動一個合併執行緒,當 Immutable Memtable 達到一定數量,合併執行緒會將其寫入磁碟(Flush),生成 Level 0 的 SSTable 檔案。
當 Level N 的 SSTable 檔案數量到達閾值之後,會進行合併壓縮(Compaction)操作,在 Level N+1 生成新的 SSTable 檔案。
SSTable 分為多層,單個檔案的大小通常是上一層的 10 倍,每層可以同時包含多個 sst file,每個檔案由多個 block 組成,其大小約為 32K,是磁碟 IO 的基本單位。
第 Level i (i > 0) 層的 SSTable 滿足:
  • 第 i 層所有檔案均由 i - 1 層的 SSTable 合併排序而來,可以透過設定閾值(檔案個數...)來控制合併的行為

  • 檔案之間是有序的,且每個檔案的 key 集合不會與其他檔案有交集(Level 0 的 SSTable 除外)

Compaction 策略

常用的 Compaction 策略有 Classic Leveled、Tiered、Tiered & Leveled 、FIFO 等,簡單介紹下前3種。

1、Classic Leveled
Classic Leveled 模式下每一層都是獨立的"Sorted Run" ,代表是按 Key 排序且同層 sst file 之間的 Key 值沒有重合,數量大小是逐層增大。相鄰的兩層 sst file 比稱之為fanout(扇出),每次做 Compaction 的條件是Ln層大小達到了閾值,將Ln層資料與Ln+1層資料進行合併。由於每次做 Compaction 都將Ln層資料寫入到Ln+1中,寫放大情況會比較嚴重, 比如L1 ,L2 兩層 fanout 是10,那麼L1層寫滿後與L2層做排序合併,重寫生成新的L2層,那麼寫放大最壞情況下等於 fanout
2、Tiered
Tiered 模式與 Classic Leveled 的區別在於每一層的sst file之間Key有重合的,每層有多個"Sorted Run",每次做 Compaction 都是同層先做合併生成一個新的 sst file 寫入到下一層中,這裡與 Leveled 最重要區別是寫入到下一層後不再需要排序合併、重寫,因為 Tiered 每層存在多個"Sorted Run",那麼寫放大最壞情況下為1。但是相比於 Leveled ,會有讀放大和空間放大會比較嚴重。
3、Tiered & Leveled

Tiered & Leveled 模式是指對於層級較小的 Level ,資料量比較小,寫入的資料較新,被更新的可能性比較大,使用 Size-Tiered 模式減少寫放大問題;對於層級較大的 Level , SSTable 的資料量較大,資料比較舊不太容易被更新,使用 Leveled 模式減少空間放大問題。

二、OceanBase 的分層轉儲

OceanBase 資料庫的儲存引擎就是基於 LSM-Tree 架構的設計,也是劃分為記憶體中的MemTable 和磁碟上的 SSTable 。OceanBase 將磁碟上的 SSTable 劃分為三層,使用的是 Tiered & Leveled 的 Compaction 策略,在 L0 層使用 Tiered 模式,在 L1 層、L2 層使用 Leveled 模式。
技術分享 | LSM-Tree 和 OceanBase 分層轉儲
OceanBase 中的 Compaction 分為三種型別:Mini Compaction 、Minor Compaction 、Major Compaction 。其中 Major Compaction 指的是大合併,我們先不談,這裡只說一下Mini Compaction和Minor Compaction

Mini Compaction (轉儲)

技術分享 | LSM-Tree 和 OceanBase 分層轉儲
Mini Compaction 是一種 Tiered 型別的 Compaction,核心就是釋放記憶體和資料日誌,記憶體中的 Frozen MemTable 透過 Mini Compaction 變成磁碟上的 Mini SSTable。
Mini Compaction 在OceanBase設計裡代表的就是一次轉儲,對應的型別是 MINI_MERGE

Minor Compaction

隨著使用者資料的寫入,Mini SSTable 的數量會逐漸增多,在查詢時需要訪問的 SSTable 數量會增多,會影響查詢的效能。Minor Compaction 就是將多個 Mini SSTable 合成一個,主要目的是減少 SSTable 的數量,減少讀放大問題。當 Mini SSTable 的數量超過閾值時,後臺會自動觸發 Minor Compaction。
Minor Compaction 細分為兩類:
1、L0 -> L0
Tiered 型別的 Compaction,將若干個 Mini SSTable 合成一個 Mini SSTable,放置於 L0 層。對應的型別是 MINI_MINOR_MERGE
技術分享 | LSM-Tree 和 OceanBase 分層轉儲
2、L0 - > L1

Leveled 型別的 Compaction,將若干個 Mini SSTable 與 Minor SSTable 合成一個新的 Minor SSTable,放置於 L1 層。對應的型別是 MINOR_MERGE

技術分享 | LSM-Tree 和 OceanBase 分層轉儲

實驗(使用社群版 OceanBase 4.0.0.0)

測試建立的租戶 ob_bench ,記憶體2G。幾個主要引數設定為

memstore_limit_percentage = 50
freeze_trigger_percentage = 20
minor_compact_trigger = 2
_minor_compaction_amplification_factor = 25
major_compact_trigger = 9999  (我們本次實驗僅是想>探索L0、L1級的Compaction,不希望觸發大合併,所以該引數設定一個極大值)

實驗一:在持續資料流的情況下,觀測L0, L1層轉儲的時機

1、建立測試庫 sysbench ,用 sysbench 工具建立1張表sbtest1、資料100W。

租戶每觸發一次轉儲 memtable dump flush 的資料必然是包含許多表的,我這裡只建立1張業務表,僅是希望後續測試時業務變更相對集中

sysbench /usr/share/sysbench/oltp_insert.lua --mysql-host=172.30.134.1 --mysql-db=sysbench  --mysql-port=2881 --mysql-user=root@ob_bench  --tables=1 --table_size=1000000 --report-interval=10 --db-driver=mysql --skip-trx=on --db-ps-mode=disable --create-secondary=off --mysql-ignore-errors=6002,6004,4012,2013,4016 --threads=10 --time=600  prepare
技術分享 | LSM-Tree 和 OceanBase 分層轉儲

先透過檢視 DBA_OB_TABLE_LOCATIONS 找到 sbtest1 對應的 TABLET_ID ,然後透過 GV$OB_TABLET_COMPACTION_HISTORY 查詢到在建立100W資料過程中,已經觸發了4次 MINI_MERGE 和1次 MINI_MINOR_MERGE

技術分享 | LSM-Tree 和 OceanBase 分層轉儲

3、對 sbtest1 持續的寫資料,觀測 sbtest1 表級的轉儲情況

sysbench /usr/share/sysbench/oltp_insert.lua --mysql-host=172.30.134.1 --mysql-db=sysbench  --mysql-port=2881 --mysql-user=root@ob_bench  --tables=1 --table_size=1000000 --report-interval=10 --db-driver=mysql --skip-trx=on --db-ps-mode=disable --create-secondary=off --mysql-ignore-errors=6002,6004,4012,2013,4016 --threads=10 --time=600  run
技術分享 | LSM-Tree 和 OceanBase 分層轉儲
技術分享 | LSM-Tree 和 OceanBase 分層轉儲
技術分享 | LSM-Tree 和 OceanBase 分層轉儲
技術分享 | LSM-Tree 和 OceanBase 分層轉儲
技術分享 | LSM-Tree 和 OceanBase 分層轉儲
技術分享 | LSM-Tree 和 OceanBase 分層轉儲
測試總結:
官方對於引數 minor_compact_trigger 的解釋:“minor_compact_trigger 用於控制分層轉儲觸發向下一層下壓的閾值。當該層的 Mini SSTable 總數達到設定的閾值時,所有 SSTable 都會被下壓到下一層,組成新的 Minor SSTable 。”  

如上測試時我們設定的 minor_compact_trigger = 2 ,按理解在每兩次觸發 MINI_MERGE 之後,就會觸發一次 MINOR_MERGE ,把L0層的 SSTable 下壓到L1層。實際測試下來發現未必如此,當達到 minor_compact_trigger 的閾值後,必然會觸發 Minor Compaction ,但它可能是L0層上的 MINI_MINOR_MERGE(同層資料合併),也可能是L0->L1層的 MINOR_MERGE(資料下壓到下一層)。但是具體什麼情況下,觸發哪種 Minor Compaction ,在官方文件只是介紹會受隱藏引數_minor_compaction_amplification_factor控制,但是具體如何影響的 也並沒有給到相應的觀測手法。

附:官網對引數 _minor_compaction_amplification_factor 的解釋:“_minor_compaction_amplification_factor 控制 L0 層內部多個 Mini SSTable 轉儲的時機,預設為 25。當所有 Mini SSTable 的總行數達到 Minor SSTable 的寫放大係數比例後,才會觸發 L1 層轉儲,否則觸發 L0 層轉儲。當 L1 層不存在 Minor SSTable  時,所有 Mini SSTable 行數到指定閾值(由 minor_compact_trigger 控制)後才會觸發 L1 層轉儲。”

實驗二:alter system minor freeze 是否真的在L1層做一個 MINOR_MERGE 型別的 compaction ?

1、同實驗一的引數配置,且minor_compact_trigger = 2

先對sbtest1表記錄做一次update,然後手動執行 alter system minor freeze ;(因為我們實驗觀測的是指定表的 merge 情況,所以在 minor freeze 之前要做一次 update 操作,主要是保證 memtable 中有對該表操作的記錄)

技術分享 | LSM-Tree 和 OceanBase 分層轉儲
如上我們看到,alter system minor freeze 之後只是做了一次 MINI_MERGE ,並沒有到L1層。

我們再執行一次 alter system minor freeze ;

技術分享 | LSM-Tree 和 OceanBase 分層轉儲
這一次我們發現在做了一次 MINI_MERGE 之後,觸發了 MINI_MINOR_MERGE 。
我們可以繼續這樣做下去,最終我們發現 alter system minor freeze 實際上做的是 MINI_MERGE ,在 MINI_MERGE 之後具體是否會觸發 MINI_MINOR_MERGE 或  MINOR_MERGE ,還是會受實驗一里面所提到的引數 minor_compact_trigger 和  _minor_compaction_amplification_factor 的控制。
2、同實驗一的引數配置,但設定 minor_compact_trigger = 0

同樣是多次執行 alter system minor freeze ,每次執行後觀察 merge 情況。

技術分享 | LSM-Tree 和 OceanBase 分層轉儲
技術分享 | LSM-Tree 和 OceanBase 分層轉儲
技術分享 | LSM-Tree 和 OceanBase 分層轉儲
可以看到在 minor_compact_trigger = 0 時,當記憶體中的 memtable dump flush 到L0層後,會立刻下壓到L1層, 這點同官方文件中的解釋是一致的。
測試總結:

透過實驗二的測試我們發現,alter system minor freeze 真正做的是形成一個 mini sstable (MINI_MERGE) ,在 MINI_MERGE 之後是否還會觸發其他的 merge ,同樣是受引數 minor_compact_trigger 和 _minor_compaction_amplification_factor 的控制。並且指令中的 minor freeze 實際上並不是特別準確,因為看到 minor 總會讓人想到L1層,如果改成 mini freeze 會更合適一些。

參考資料
https://www.cnblogs.com/buttercup/p/12991585.html
https://www.oceanbase.com/docs/community-developer-advance-0000000000634015

本文關鍵字:#OceanBase# #分層轉儲#

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70024420/viewspace-2938988/,如需轉載,請註明出處,否則將追究法律責任。

相關文章