天天看點

LSM優化系列(五) -- 【SIGMOD‘19】X-engine 在電商場景下針對大規模事務處理的優化-- 強者恒強啊

文章目錄

  • ​​1. 前言​​
  • ​​2. 論文結構​​
  • ​​2.1 海嘯 問題​​
  • ​​2.2 洩洪 問題​​
  • ​​2.3 洋流 問題​​
  • ​​3. X-engine架構​​
  • ​​3.1 讀路徑優化 概覽​​
  • ​​3.2 寫路徑優化概覽​​
  • ​​3.3 Flush和Compaction​​
  • ​​4. X-engine 優化細節​​
  • ​​4.1 讀路徑上的優化​​
  • ​​4.1.1 Extent的設計​​
  • ​​4.1.2 Cache的優化​​
  • ​​4.1.3 多版本中繼資料索引的設計 -- Extent複用​​
  • ​​4.1.4 Cache的增量替換​​
  • ​​4.2 寫路徑上的優化​​
  • ​​4.2.1 多版本memtable的實作​​
  • ​​4.2.2 異步寫事務​​
  • ​​4.2.3 多級流水線​​
  • ​​4.3 Flush和Compaction​​
  • ​​4.3.1 快速重新整理L0中的warm extents​​
  • ​​4.3.2 Compaction 加速​​

1. 前言

論文原位址

​​​X-Engine: An Optimized Storage Engine for Large-scale E-commerce Transaction Processing​​​ 中文介紹

​​x-engine最佳實踐​​

因為本篇論文本身就是阿裡重寫了LSM架構,在原有rocksdb實作的基礎上做了非常多的優化,這裡如果對rocksdb整體實作沒有足夠的了解,就體會不到X-engine優化思想的精妙之處。

如果想要詳細了解rocksdb的各個子產品的實作,可以參考如下幾篇文章:

​​1. Rocksdb 寫流程,讀流程,WAL檔案,MANIFEST檔案,ColumnFamily,Memtable,SST檔案原理詳解​​2. Rocksdb compaction源碼剖析 (一): SST檔案詳細格式解析

3. Rocksdb compaction源碼剖析 (二):整體實作概覽

4. Rocksdb WAL 實作詳解

5. Rocksdb DeleteRange實作原理

6. Rocksdb 性能分析和調試的一些思路和方法

7. Rocksdb 事務和隔離級别的實作

ps: 當然有基礎的夥伴可以進入正題,一起讨論哈

沒有基礎的夥伴可能需要簡單概覽一遍,會花費較多時間,不過有趣也很枯燥。。。。哈哈哈

不得不說一群大佬聚集在一起還是能夠産生非常優秀的思想和産品的,将一個 資料引擎做到極緻,每一個優化細節都精心雕琢,還是能夠吐出非常可觀的性能和延時名額的。

即使在如今LSM 讀上一直被人诟病的情況下,X-engine逆流而上拿出了屬于自己的優化政策,進而實作了X-engine寫性能優于innodb,而讀性能僅僅比innodb差一點的性能,這在LSM的引擎架構下實屬難得。

X-engine畢竟是自研的,而且作為Polordb-X的存儲引擎,必然是要走收費路線了,底層代碼肯定是看不到的,不過整個基于LSM 優化實作的思想已經在論文中一一展現,非常詳細,想必也能為自己在現有rocksdb引擎的基礎延伸一些思考。

2. 論文結構

整個論文所描述的阿裡雲想要解決的問題 被cheng 博士歸納為三個問題(不得不說學術大佬的抽象思維能力真是強):

  • 海嘯
  • 洩洪
  • 洋流

論文後續的優化細節描述以及測試和展望 都是圍繞如何更好得解決以上三個問題展開的。

接下來簡單為大家描述一下cheng 博士歸納這三個問題的背景以及每個 問題對應那一些具體業務上的痛點,而阿裡龐大使用者基數以及資料量的支撐下所展現出來的問題便能夠暴露出一套存儲系統在各個緯度上設計的不足。

1. 淘寶天貓 使用者交易資料

在阿裡 淘寶和天貓 都會存儲使用者交易資料,我們的這一些資料在阿裡雲伺服器上是需要永久儲存的,進而提供曆史記錄讓使用者查詢,交易叢集面臨如下問題:

  • 資料條目過萬億,磁盤容量達到TB級别
  • 在大促銷時會面臨巨大的冷熱資料交替通路的壓力

而想要性能可以通過拆分資料庫,将一部分資料遷移到新的叢集,理論上是能夠實作容量和性能的無限擴充。但是叢集數量的增大也為整個叢集的運維成本帶來巨大的複雜度,而且存儲成本仍然在不斷增加。

為了降低存儲成本,并提升促銷時的性能 ,這一些核心業務引入了X-engine。

2. 釘釘 聊天記錄資訊庫

同樣,釘釘作為中國領先的企業辦公通信軟體,需要實作聊天記錄的永久儲存,并且提供多端漫遊功能。

随着使用者的爆發式增大,達到億級别。整個系統的存儲成本代價較大,如果在不影響現有innodb二級索引以及事務處理功能的前提小進一步降低存儲成本,引入了X-engine。

3. 阿裡圖檔資訊庫

阿裡巴巴集團圖檔空間是淘寶智能圖檔中心面向商家提供的免費圖檔存儲管理服務,本身淘寶天貓累積的商家圖檔資訊已經非常龐大, 随着雙十一和雙十二購物節前後,商家又會大量上架新的商品圖檔,圖檔資訊資料會爆發式增長。

圖檔的索引資訊是通過URL的,非常适合字首壓縮(rocksdb)。為了進一步節約存儲成本,保證請求時延 、吞吐和innodb沒有太大差異的前提下,阿裡引入了X-engine來作為圖檔資訊資料庫的存儲引擎。

可以看到X-engine是為特定的業務場景設計的,能夠将自己的優勢完全展現出來。即使這樣,在阿裡龐大的資料量支撐下,X-engine也創造了巨大的收益。

回到cheng博士歸納的三個問題:海嘯、洩洪、洋流,

2.1 海嘯 問題

阿裡巴巴的淘寶和天貓有全球使用者都會參與的購物節,像是雙十一,雙十二這樣的。

這一些購物節在會在午夜零點開始,一般會持續一天時間。而對于午夜零點那一刻有,整個系統的資料會以百倍千倍的的壓力奔向伺服器,同時衍生的數量更為龐大的事務workload 更是像人類的海嘯一樣拍向海岸。

LSM優化系列(五) -- 【SIGMOD‘19】X-engine 在電商場景下針對大規模事務處理的優化-- 強者恒強啊

如上圖中,論文展示的整個系統的吞吐在雙十一零點時的表現,紅色的曲線代表的是系統的吞吐,高于平時的122倍。

而藍色的曲線代表的是請求時延情況,阿裡的x-engine即使流量暴增的情況下仍然保持較為穩定的時延。

也就是在海嘯場景下,引擎的海岸要足夠堅硬高大,承接住這樣的壓力。

2.2 洩洪 問題

同樣是在海嘯場景下,我們要将關注重心轉移到引擎層的處理過程。引擎需要有快速搬遷資料的能力,快速得将資料從主存中搬遷到底層持久化儲存設備中。進而将高速的主存空間空出來接受上層海量并發的事務處理。

即使如今我們主存的容量仍然在穩定得增加,但是在面對海量的寫入、更新操作的過程中主存容量還是捉襟見肘。是以,這個時候利用已有的儲存設備RAM, SSD, HDD 來搭建一套按照資料熱度分層存儲的系統達到“洩洪”的目的。

這也就是X-engine冷熱分離的思想,按照資料熱點高低,将資料分布在速度比較快的RAM,比較高速的持久化裝置SSD以及最慢的,儲存熱度最低資料的HDD。同時一些新的記憶體技術,像是NVM也能夠應用到整個分層的過程。

在此x-engine 所做的存儲引擎架構選型就抛棄了傳統的B樹結構,采用天然适配冷熱分離的存儲架構的LSM 結構。而且LSM擁有常駐記憶體的資料結構(memtable)能夠以追加寫(append-only)方式快速插入,再加上分層常駐于磁盤的結構就為洩洪問題的解決在軟體層面做了技術上的架構鋪墊。

2.3 洋流 問題

對于大多數資料庫場景,熱點資料會被持續通路一段時間。而在電商促銷場景下的workload,會出現冷熱資料的交替變化(我們買一件促銷衣服,而下面又會推薦對應的鞋子,衣服剛開始促銷時是熱點資料,鞋子過了一會從熱點資料變為冷資料),這裡的冷熱交替是指資料加載到高速裝置上,又被迅速洩洪到底層低熱度裝置。

如果将資料庫的cache(存儲熱度最高的裝置RAM)看作海面,下層持久化的資料庫看作深海,那麼這樣的冷熱交替過程就像是洋流一樣将深海冷的水流帶到海面,将海面溫度高的水帶入到深海。

是以x-engine在這樣的場景下需要盡可能快得完成深水的上升,并快速得将他們緩存到高速裝置(cache)。

通過以上X-engine論文中形象概括的三個問題,整個業務痛點就非常清晰了,而目标定下來,接下來就是整個解決過程了。

3. X-engine架構

LSM優化系列(五) -- 【SIGMOD‘19】X-engine 在電商場景下針對大規模事務處理的優化-- 強者恒強啊

如上圖 是引擎中對x-engine整體架構的描述。X-engine的定位除了之前部分也描述一部分也是為了處理OLTP類型的資料(線上事務處理 – 電商促銷平台),主要是解決三個主體問題,是以這裡從架構設計上也能看出來。整體的優化相比于傳統LSM 架構主要展現在三個方面:讀路徑,寫路徑,Flush和Compaction具體的優化點 論文中已經整理出來一個表格如下:

LSM優化系列(五) -- 【SIGMOD‘19】X-engine 在電商場景下針對大規模事務處理的優化-- 強者恒強啊

3.1 讀路徑優化 概覽

讀路徑是指 從存儲中将讀記錄取出來傳回給用戶端 這一段路徑。傳統的LSM 在讀性能上并不友好,因為它要先從memtable中讀,memtable沒有則會去底層LSM 中一層一層去查找,最為耗時的情況是從最後一層讀到資料但是發現這個key并不存在。

Rocksdb針對以上讀場景已有的優化是

  • MANIFEST (這個檔案儲存的是整個db在一段時間内的快照,有SST檔案更新,建立,删除都會将SST檔案相關的資訊更新到這個檔案中) 中會儲存所有的sst檔案的start-end key,然後查找時是需要确定key所屬檔案包含的key的範圍。
  • BloomFilter 存放在blockcache記憶體中 ,用來百分百判斷一個key不存在,機率性的判斷一個key存在。

X-engine在針對key的點查 性能行進一步優化(因為電商平台促銷時會産生大量的點查事務)。這裡是針對x-engein 設計的有序位元組表 extent的優化,維護一個 metadata index 結構用來索引extents和memtable。将多個metable index存放在cache中,查找的時候隻需要通過cache 中的 metaindex快速索引到具體的extent 即可。

同時也做了一個增量的接口來對cache中的過時資料 進行高效的替換(我們知道compaction的過程會不斷的生成删除sst檔案,這一些更新的sst檔案中的datablock記錄也會被從blockcache中清理掉)來減少compaction過程中對blockcache中不必要替換的key進行更新(僅僅更新了一個datablock中的幾個key,卻要将整個datablock替換掉)。

這裡僅僅是一個整體優化的概述,論文會在後續的細節描述部分進一步讨論。

3.2 寫路徑優化概覽

寫路徑除了包括實體裝置的IO,還有引擎層面的一些元件的插入和更新。

寫請求會先寫入redo logs(類似于rocksdb 的wal),完成之後會寫入active memtable,達到門檻值之後active memtable被标記為 隻讀 ,變為immutable memtable進行flush,同時建立一個空的active memtable繼續快速接受請求。

X-engine 為了平衡IO的高延時(寫WAL)和memtable寫入的低延時,做了多級流水線,每一個線程可以處理任何一個階段的寫wal,在事務請求足夠大的時候幾乎能夠将整個寫wal和寫memtable并行起來,提升了整個寫入的吞吐。

3.3 Flush和Compaction

這裡之是以兩個機制的優化放在一塊,因為他們都是異步執行的,同時也都是為了生成新的sst檔案。Flush是将immutable memtable中的資料寫入到L0形成sst檔案。Compaction是對可能重疊的SST 檔案中的key進行去重,也是為了清理delete ,多次put ,以及merge操作的key。

Compaction會存在于LSM中的不同層,挑選Ln的多個sst檔案,再加上Ln+1層與上層重疊的key的sst檔案,一起讀到記憶體中作為一次compaction進行合并,将最終合并的結果再寫入到新的SST檔案之中。

這個過程會消耗比較多的CPU和IO,同時有較多的寫放大問題,可能存在一個key反複被compaction了多次即使這個key的資料最終還是和原來一摸一樣。

X-engine為了優化以上compaction和flush過程中的問題,做了如下幾個方面的優化

  • 優化了memtable flush到L0的過程
  • 精細管理extent達到資料複用的目的,來降低需要被merge的extents數量
  • 通過異步IO 來對重疊的extents 進行合并
  • 解藕compaction到FPGA降低CPU 的消耗

4. X-engine 優化細節

X-engine 這裡将針對事務的優化處理放到了第一個描述的位置,這裡可能事務的優化對整體引擎的吞吐提升巨大,值得提前讓讀者體驗。

X-engine通過MVCC(多版本并發控制)和2PL(兩階段鎖)來實作對應的隔離級别: SI(snapshot Isolation)和RC(read commited)。 這兩個級别其實也是rocksdb支援的隔離級别,關于這兩個隔離級别的細節描述可以回到文章開頭,有一篇關于rocksdb事務隔離級别的實作介紹,簡單了解一下。

不同版本(rocksdb中的LSN)相同的user key會被自動增加的ID隔離。每一個輸入到X-engine的事務讀的時候也僅僅隻能看到它之前的LSN,每一個LSN都可以作為自己的snapshot功能。每一個寫入的事務也都會增加一個行鎖(row lock)來比避免事務之間的寫沖突。

整體優化後的 事務處理架構如下:

LSM優化系列(五) -- 【SIGMOD‘19】X-engine 在電商場景下針對大規模事務處理的優化-- 強者恒強啊

如上架構中,整個事務的處理由兩部分組成(左側部分):Read/Write 階段 和 Commit 階段。

所有的讀請求都會通過read phase再進入讀路徑。在這個階段,讀請求會被當作一個事務 插入/更新 到事務緩沖區之中;接下來進入commit 階段,任務繼續從事務緩沖區中進行排程,将請求寫入到分布式的write 隊列中。最後由多級流水線 對後續的資料請求寫入redo log和 插入到LSM中進行 并發排程。

4.1 讀路徑上的優化

4.1.1 Extent的設計

論文中寫介紹了X-engine 設計的底層存儲結構 – extent (類似與rocksdb的sst)

LSM優化系列(五) -- 【SIGMOD‘19】X-engine 在電商場景下針對大規模事務處理的優化-- 強者恒強啊

extent 和sst一樣,主體還是由多個datablock組成,每個datablock包含具體的key-value資料,還有一個index block,用了索引目前extent 中的多個 datablock 結束key的位置,用于加速查找。

和sst差異的地方就是多了一個Schema data。這個資料區域為了支援上層的DDL語言,來加速電商平台中頻繁變更資料庫schema資訊的場景,這個資料區域的設計對提升電商平台的資料庫按主鍵查找、插入等依賴schema資訊的請求性能 有非常巨大的提升。

整個extent 被設計為2MB的大小,這個2M的來源是經過不斷測後續compaction中的extents複用功能 以及 cache替換 的優化 得到的最優的大小。

4.1.2 Cache的優化

接下來就是讀路徑上的具體優化了,首先看看cache的優化:

LSM優化系列(五) -- 【SIGMOD‘19】X-engine 在電商場景下針對大規模事務處理的優化-- 強者恒強啊

row cache的設計主要用來加速點查性能。row cache使用LRU 作為資料頁的替換政策,這裡不會關心具體的資料區域是否存在于LSM中。是以在LSM的最大層中的冷資料也可以被緩存到row cache中,這樣能夠加速冷資料的通路。此外點查過程中沒有從memtable中找到,那麼這個key最後 會被緩存到row cahce中,下次查找隻需要O(1)的時間。

有一個問題是row cache對随機key場景并不友好,别緩存的key不一定經常通路。

在電商平台中,由于計算機系統的局部性原理,最新的請求大機率是會被繼續通路的,是以row cache 盡可能儲存最新的請求。為了實作這個功能,這裡會在memtable flush時候将最新的請求更新到row cache中,強行将舊的請求淘汰掉。

block cache中主要緩存 以data block為機關的資料。儲存row cache中淘汰的資料來降低cache miss,或者加速range 查找。其中table cache隻要儲存extent的中繼資料資訊來定位具體的extent的位置,當extend加載到tablecache之中後會構造對應的bloom filter ,加速對不存在的key的過濾。如果key存在,則通過block cache緩存的index block查找對應的datablock資料。

整個cache的設計還是為了加速熱點資料的通路,多級cache 為了降低cache -misses的效果。可能整體 的cache管理變得複雜多變,但是相比于讀路徑走一次IO的開銷,這一些CPU計算的耗時就微不足道了,還是有可以借鑒的地方的。

4.1.3 多版本中繼資料索引的設計 – Extent複用

接下來介紹了多版本中繼資料索引的管理方式,設計如下圖:

LSM優化系列(五) -- 【SIGMOD‘19】X-engine 在電商場景下針對大規模事務處理的優化-- 強者恒強啊

LSM-tree 中的subtable 會有一個與它相關聯的metadata index,當有一些改動落到了目前metadata index所關聯的key-value範圍内時,會為這個改動生成一個新的metaSnapshot,并将這個snapshot插入到與它相關聯的level之中(這個過程是通過Copy on write 機制來做的)而不需要更改其他的metaSnapshot的連結。

如上圖,extenti 是屬于level0層,而且被緩存到了blockcache當中。當compaction 過程中需要完全複用這個extent的時候,一個新的MetaSnapshot (v+1) 會通過metaSnapshot (v) 重新生成,并且連接配接到compaction的輸出層level1。這裡有一個相比于原本rocksdb 的 compaction的實作優化之處是目前的輸出操作并不需要真正将extenti 從level0移動到level1,而是變更MetaLevel1的指針,指向Extenti即可。

是以這個通過MetaIndex管理的extent樹 ,加上可複用的extent,能夠極大得降低L0以上層之間Compaction的寫放大問題。

4.1.4 Cache的增量替換

還有一處讀路徑上的優化是Compaction合并檔案過程中的Cache替換政策的優化。

LSM-tree原本的Compaction實作邏輯是在合并SST檔案之後進行大量的cache替換,将block-cache中的參與過compaction輸入的檔案都替換為輸出,即使cahce中存放的datablock輸入輸出前後并沒有發生變化也會做一次cache 置換。這個過程會造成電商促銷場景下的大量cache失效,降低讀性能。

為了追蹤這個問題,減少compaction過程中對未發生變化的datablock進行替換進而導緻的cache-miss 比率,X-engine提出了增量替換block cache的政策。這裡主要的實作過程是 Compaction過程僅僅對發生了合并且緩存在blockcache中的datablock進行替換,将老的datablock替換為合并後新的datablock,而不是針對整個extents所屬的datablock進行全量替換。

4.2 寫路徑上的優化

X-engine描述了總體的寫路徑上的優化,報錯從寫入請求落到由LSM-tree管理的sub-table的過程,write task queue的設計 以及 優化最為徹底的多級流水線的實作細節。

4.2.1 多版本memtable的實作

X-engine 改造了memtable的資料結構,将skiplist改造為無鎖skiplist 來加速插入和查找性能,這個改造在其他類似的系統中也實作了。

存在的問題是如果系統中存在資料熱點,那麼會經常插入單個相同的記錄,隻是會有多個版本,而使用者隻會讀取最新的版本。而查找的過程則需要過濾掉大量的老版本的記錄才能找到對應的記錄。

X-engine為了解決上述多版本key的問題,追加一個新的版本,豎直連接配接到目前版本最後一個key的下面。

LSM優化系列(五) -- 【SIGMOD‘19】X-engine 在電商場景下針對大規模事務處理的優化-- 強者恒強啊

如上圖,藍色節點代表跳表。跳表中儲存的是key的目前版本,而綠色節點之下則是key300的上一個版本。後續有關于key-300的其他版本,則采用單連結清單的頭插法插入到key300跳表節點之下。

這樣的設計能夠降低查找key時 多個版本重複通路的消耗。

4.2.2 異步寫事務

在正常的單線程單事務 處理寫請求的存儲引擎中(像innodb),都會在寫的延時上耗時較多。一個使用者線程完成一個事務從開始到結束的所有處理過程。如果使用者不需要寫請求的WAL(redo log)落盤,這個方法很容易實作寫請求的并發控制,。如果使用者需要寫WAL,那麼這樣的單線程單事務的處理模式會導緻線程長時間等待IO的情況,極為低效。

而X-engine這裡有兩個層面的優化:

  • 分開送出對應事務的寫入
  • Batch 寫

大體的實作方式和目前rocksdb的writer 類似,即多個線程并發寫入,會為每一個線程建立一個writer。根據writer的建立時間 選擇主writer ,其他線程的writer都會被标記為從writer,這個時候由主writer将所有請求的WAL資料彙總,batch write到底層,主writer寫完之後所有的writer開始并發寫事務,後續的事務處理就交給多級流水線來做了。

在這個場景下如果 上層的并發足夠高,可以讓後續的多級流水線完全并行起來,極大得提升吞吐。

4.2.3 多級流水線

在整個寫路徑上 會産生多個寫入序列(生成事務,寫WAL, 寫memtable),當有并發線程的時候這一些序列可能會同時通路主存和磁盤,同時執行過程也伴随着大量的計算。是以很難通過計算來減少記憶體的通路和磁盤IO次數。

為了将計算和記憶體通路/磁盤IO 合理利用起來,X-engine實作了多級流水線的功能。如下圖是整個四級流水線的形态:

LSM優化系列(五) -- 【SIGMOD‘19】X-engine 在電商場景下針對大規模事務處理的優化-- 強者恒強啊
  • Log-buffering 這一級主要是将線程從trasaction buffer中收集到的WAL 寫入到log buffer中,并做一些CRC的校驗。

    在這一級 有比較多的計算和記憶體通路(不會産生IO)

  • log-flushing 這一級會将log buffer中的資料寫入到磁盤。并且會生成一個對應請求的lsn,這一級會産生IO
  • writing-memtabls 這裡線程會并發将資料寫入到active memtable中,僅僅會通路記憶體。

    當主存失效時 會從之前寫入的log buffer (WAL)中進行資料重放,保證了memtable寫入的crash recovery

  • Commits 最後一級,線程會并發送出事務的寫入完成,并且會釋放前面線程處理過程中所占用的記憶體和鎖等資源。

X-engine為了合理的配置設定多級流水線處理過程中的線程資源,将第一和第二級的處理僅配置設定一個線程(寫WAL需要先保證羅盤才行,單線程可以批處理),第三級之後都能夠并發處理,則從第三級開始就配置設定了多個線程完成後續的操作。

4.3 Flush和Compaction

4.3.1 快速重新整理L0中的warm extents

在X-engine之中通過flush過程将記憶體中的資料重新整理到磁盤上,進而降低系統在大量的事務請求壓力下産生的OOM的風險。X-engine中每一次flush操作都會将隻讀的memtable(immutable memtable)不經過排序,直接生成extents,并追加到L0之上。在電商平台中,剛寫入的資料很可能會别再次讀取到,是以x-engine這裡将L0的作為warm層,即使L0的大小隻占整個LSM的1%。

X-engine為了保證L0的熱度,将L0中真正參與compaction的輸出的extents檔案保留在L0,并不會将他們下刷到L1,進而保證warm資料能夠持續留在更高層。

4.3.2 Compaction 加速

Compaction的過程包含了非常耗時的merge操作,X-engine為了加速 compacion操作做了三種優化:

  • 資料複用
  • 異步IO
  • 解除安裝compaction到 FPGA

接下來一一看看X-engine的這三種針對compaction的優化,非常有借鑒意義。

資料複用 技術

這個我們在分析X-engine讀路徑上的 Cache增量替換的過程中有提到過這個優化,以如下圖為例:

LSM優化系列(五) -- 【SIGMOD‘19】X-engine 在電商場景下針對大規模事務處理的優化-- 強者恒強啊

單個Extent的存儲結構上面也描述過了,包括data block ,schema data, index block三部分。X-engine為了增加Extent的服用機會,讓extent更加高效,将Extent的大小設定為了2M,每一個datablock的大小是16KB。

如果一個Extent的key的範圍剛好屬于 參與Compaction的key的範圍,而這個extent并沒有和其他參與compaction的extent 的key ran ge重疊,那麼這個extent可以被當作可複用的extent。隻需要為該Extent建立一個metadata index,更新到參與comoaction的metasnapshot 節點樹之上。該extent不會産生一次IO,進而降低寫放大。

舉個例子:

這裡有一次compaction,參與的層分别是L1和L2

L1中有三個extents 參與compaction: [1,35],[80,200], [210,280]

L2中有五個extents 參與: [1,30], [50,70]. [100,130], [150,170], [190,205]

如下是在不同場景下的 extent複用情況

  • L1的[210,280]和 L2的[50,70]能夠被直接複用 – 在compaction的range範圍内且和其他的extent沒有重疊
  • L1的[1,35] 和 L2的[1,30]有重疊,但是隻有L1的[1,25]datablock和L2的[1,10]data block有重疊,是以L1的data block [32,35] 能夠被複用。
  • L1的[80,200]和L2的多個extent有重疊,它的第二個datablock和三個L2的extents有重疊。但是這個datablock的[135,180]區間并沒有key,是以X-engine這裡将這個datablock拆分成兩個datablock: [106,135], [180,200],這兩個datablock和L2的[100,130],[190,205]進行合并。這樣L2的[150,170]這個datablock就能夠被複用。

通過這樣細粒度得管理,降低compaction過程中IO的次數,在事務密集型場景能夠極大得降低compaction對上層吞吐的影響(記憶體操作相比于磁盤IO 肯定節約不少)。

異步IO技術

在extent寫入到具體LSM的層的過程中主要分為三個階段:

  • 從storage 層讀入extents
  • 記憶體中進行合并
  • 将合并後的一個或者多個extent重新寫入LSM之中

其中第一和第三階段都是IO操作,第二階段是記憶體操作。是以X-engine這裡将異步IO的發力點放在了第一和第三階段,将第一和第三階段的IO操作變更為異步IO。第二個階段是在第一階段讀入extents完成之後進行回掉的結果。

當并發進行Compaction的時候 第二個執行階段會 會被隐藏,防止出現重複處理。(rocksdb在這裡的處理邏輯是目前排程的compaction會對參與處理的sst檔案進行标記,後續的compaction線程檢測到該标記,便不會将該檔案添加到compaction的處理之中)

FPGA offloading

這個技術是結合新硬體FPGA 将compaction offload到CPU上,進而降低compaction對CPU和影響,也進一步降低compaction對上層吞吐抖動的影響。

這裡之是以選擇compaction,也是因為Compacion本身是一個相對獨立的操作,在兩個連續的層之間處理extents。将這個過程單獨通過FPGA以流處理的方式讀入,再按照對應的extents格式由FPGA核心經過Cmodule寫出。

關于Compaction on FPGA的細節可以參考論文

​​FPGA-Accelerated Compactions for LSM-based Key Value Store​​

到現在為止,基本的X-engine在LSM上的優化已經說的差不多了,大佬們的很多思想還是能夠借鑒的。

論文的後續就是一些優化後的性能評估,評估的政策還是按照開頭提出的三個電商促銷平台的主體問題:

  • 海嘯
  • 洩洪
  • 洋流