記憶體資料庫解析與主流產品對比(三)

星環科技發表於2021-02-02

作者:實驗室小陳/大資料開放實驗室

在上一篇文章 《記憶體資料庫解析與主流產品對比(二)》 中,我們從資料組織和索引的角度介紹了記憶體資料庫的特點和幾款產品的技術實現。本文將繼續解析記憶體資料庫,從併發控制、持久化和查詢處理的角度介紹幾款技術,帶來更多維度、更細緻的記憶體資料庫技術討論。

— 資料庫管理系統中的併發控制

1. 記憶體資料庫併發控制的兩種策略

a. 多版本的併發控制

記憶體資料庫中的併發控制主要採用兩類策略:1. 多版本的併發控制;2. 分Partition處理。併發控制機制可以分為樂觀和悲觀兩種型別。悲觀併發控制則認為程式競爭資源總是存在的,因此訪問時先加鎖,訪問完再釋放;樂觀併發控制認為大多數情況不需要競爭資源,只在最後提交前檢查是否存在衝突,有衝突就回滾,沒有就提交。

樂觀併發控制大多數不採用基於鎖的技術實現,並且通常是多版本的。多版本意味著每次更新都會產生新的版本,讀操作根據可見範圍選取合適的老版本,讀操作不阻塞寫操作,所以併發程度比較高。其缺點是會產生額外開銷,例如更新要建立新版本,而且隨著版本越來越多,還需要額外開銷收回老版本。記憶體資料庫多采用樂觀的多版本併發控制機制,相比於基於鎖的悲觀併發控制其優勢是開銷較小,而且支援併發程度較高的場景;缺點是在有大量寫競爭的場景下,事務間衝突機率比較高時,大量事務會失敗和回滾。


b. 分Partition處理

記憶體資料庫併發控制的另外一類策略是把資料庫分成多個Partition,每個Partition採用序列方式處理事務。優勢是單Partition業務的執行沒有用於併發控制的額外開銷,缺點是存在跨Partition事務時系統的吞吐率會直線下降。因此,如果不能保證所有業務都是單Partition進行,將導致效能不可預測。


2. 多版本併發控制之 Hekaton

Hekaton採用樂觀的多版本併發控制。Transaction開始時,系統為事務分配讀時間戳,並將Transaction標記為active,然後開始執行事務,在操作過程中系統記錄被讀取/掃描/寫入的資料。隨後,在Pre-commit階段,先獲取一個結束的時間戳,然後驗證讀和掃描資料的版本是否仍然有效。如果驗證透過,就寫一個新版本到日誌,執行Commit,然後把所有的新版本設定為可見。Commit之後,Post-Processing記錄版本時間戳,之後Transaction才真正結束。

a. Hekaton 的事務驗證

i) Read Stability: Hekaton系統能夠保證資料的讀穩定性(Read Stability),比如交易開始時讀到的每條記錄版本,在Commit時仍然可見,從而實現Read Stability。


ii) Phantom Avoidances: Phantom指一個事務在開始和結束時執行相同的條件查詢,兩次結果不一樣。出現幻影的原因是該事務執行過程中,其他事務對相同資料集進行了增加/刪除/更新操作。應該如何避免幻影現象呢?可透過重複掃描,檢查所讀取的資料是否有新版本,保證記錄在事務開始時的版本和在結束時一致。


Hekaton併發控制的好處在於,不需要對Read-Only事務做驗證,因為多版本能夠保證事務開始時的記錄版本在結束時依然存在。對於執行更新的事務,是否做驗證由事務的隔離級別決定。例如如果快照隔離級別,就不需要做任何驗證;如果要做可重複讀,就要做Read Stability;如果是序列化隔離級別,既要保證Read Stability,又要保證Phantom Avoidance。


b. Hekaton的 回收策略

Hekaton中的回收任務並不由獨立的執行緒處理,而是每個事務自己回收。如下圖所示,Transaction ID為250的事務結束時間戳為150且狀態為terminated,此時會有一個Write Set獲取所有老版本,並判斷當前所有active的Transaction的開始時間戳是否大於ID為250的事務結束時間,即150。如果都大於150,說明不可能再基於時間戳早於150的舊版本進行修改,因而由事務回收舊版本,這部分工作是每個執行緒在處理Transaction時的額外工作。


3. 多版本併發控制之Hyper

Hyper的併發控制和Hekaton的區別主要有以下三點:1. 直接在記錄位置進行更新,透過undo buffer來儲存對資料的修改,資料和所有修改被連結在一起;2. 驗證是根據最近的更新與讀的記錄進行比較來實現(後續會涉及到);3. 序列化處理commit,對提交的事務進行排序,並依次處理。

在事務驗證方面,Hyper的驗證需要在日誌中記錄Read Predictates,包括查詢或Range Scan,而且要記錄插入、刪除和更新的記錄。在Hyper模式中,插入/刪除/更新透過對應的Undo Buffer獲悉被修改過的記錄,所以記錄改動對於Hyper而言是容易的。


對於每個Transaction,只需要比較該事務從開始到Commit之間,是否存在其他Transaction對滿足搜尋條件的資料集進行過增/刪/改,從而判斷是否存在幻影現象,如果存在,就直接終止事務。


4. 多版本併發控制 之HANA和 HStore/VoltDB

HANA並行控制方式比較簡單,採用悲觀的多版本控制,由行級鎖保護資料結構,每行由時間戳決定每個版本的可見範圍。每個Transaction在更新或刪除時都需要申請寫鎖,而且要做死鎖檢測。


HStore/VoltDB是一個Partition系統,鎖的粒度很粗,每個Partition對應一把鎖,因此Transaction在某節點上執行時,需要拿到該節點所有資源。一旦一個事務可能涉及到兩個Partition,就需要把兩個Partition的鎖都拿到。所以Partition系統的優點是單Partition處理速度非常快,缺點是多Partition效率很低。同時,系統對於負載的偏斜非常敏感,如果有熱點資料,那麼熱點資料就構成系統瓶頸。


5. 多版本併發控制之負載預知

假設一個工作負載中,事務需要讀和寫的資料集可以提前獲得,就可以在執行前確定所有事務的執行順序。Calvin就是基於這樣的假設設計的VLL (Very Lightweight Locking)超輕量級鎖資料庫原型系統。通用場景的工作負載是無法提前知道讀寫集合的,但在儲存過程業務的應用中,可以提前確定讀寫集合,對於這些場景就可以考慮類似Calvin的系統。
— 資料庫管理系統中的持久化技術—

對於記憶體資料庫而言,和基於磁碟的資料庫相同也需要日誌和Checkpoint。Checkpoint的目的是恢復可以從最近的檢查點開始,而不需要回放所有資料。因為Checkpoint涉及寫入磁碟的操作,所以影響效能,因此要儘量加快相關的處理。


一個不同是記憶體資料庫的日誌和Checkpoint可以不包含索引,在恢復時透過基礎資料重新構造索引。記憶體資料庫中的索引在恢復時重新構造,構造完成後也放在記憶體中而不用落盤,記憶體索引資料丟失了再重構即可。另外一個不同是記憶體資料庫Checkpoint的資料量更大。面向磁碟的資料庫在Checkpoint時,只需要把記憶體中所有Dirty Page寫到磁碟上即可,但是記憶體資料庫Checkpoint要把所有資料全部寫到磁碟,資料量無論多大都要全量寫一遍,所以記憶體資料庫Checkpoint時寫入磁碟的資料遠大於基於磁碟的資料庫。


Hekaton Checkpoint

對於持久化的效能最佳化,第一要保證寫日誌時的高吞吐量和低延遲,第二要考慮恢復時如何快速重構整個資料庫。Hekaton的記錄和索引存放在記憶體,所有操作寫日誌到磁碟。日誌只記錄資料的更新,而不記錄索引的更新。進行Checkpoint時,Hekaton會從日誌中恢復,並根據主鍵範圍並行處理。如下圖,分三個主鍵範圍:100~199、200~299、300~399,綠色代表資料,紅色代表刪除的記錄(單獨儲存被刪除的檔案)。在恢復時,Hekaton用並行演算法在記憶體中重構索引和資料,過程中根據刪除記錄過濾資料檔案,去除被刪除的資料,然後從Checkpoint點開始,根據日誌回放資料。

其他系統的Checkpoints

1. 採用Logic Logging的系統如H-Store/VoltDB,即不記錄具體的資料改動,而是記錄執行過的操作、指令。它的優勢是記錄的日誌資訊比較少。寫日誌時,HStore/VoltDB採用COW(Copy-on-Write)模式,即正常狀態是單版本,但在寫日誌時會另外“複製”一個版本,待寫完再合併版本。


2. 另一種是定期把Snapshot寫入磁碟(不包括索引),比如Hyper就是基於作業系統Folk功能來提供Snapshot。

— 資料庫管理系統中的查詢處理—

傳統的查詢處理採用火山模型,查詢樹上的每個節點是一個通用的Operator,優勢在於Operator可以任意組合。但Operator拿到的記錄只是一個位元組陣列,還需要呼叫另一個方法來解析屬性以及屬性型別。如果這種設計放到記憶體資料庫中,屬性以及型別的解析都是在Runtime而非編譯時進行的,會對效能產生影響。


另外對於get-next,如果有百萬個資料就要呼叫百萬次,同時get-next通常實現是一個虛擬函式,透過指標呼叫,相比直接透過記憶體地址呼叫,這些都會影響效能。此外,這樣的函式程式碼在記憶體中的分佈是非連續的,要不斷跳轉。綜上,傳統DBMS的查詢處理方式在記憶體資料庫當中並不適用,尤其體現在在底層執行時。

記憶體資料庫通常採用編譯執行的方式,首先對查詢進行解析,然後最佳化解析後的語句,並生成執行計劃,然後根據模板對執行計劃進行編譯產生可執行的機器程式碼,隨後把機器程式碼載入到資料庫引擎,執行時直接呼叫。

下圖是對不同查詢方式的耗時分析,可以看出編譯執行方式中Resource Stall的佔比很少。

另外一張圖解釋了目前的CPU架構實現,L2 Cache和主存之間存在Hardware Pre-fetcher。L2 Cache分為指令Cache和Data Cache,指令Cache會由Branch Prediction實現分支預測,Data Cache會由基於Sequential Pattern的Pre-fetcher實現預測。因此,資料庫系統的設計需要考慮該架構下如何充分發揮Pre-fetcher功能,讓Cache可以不斷為 CPU計算單元提供指令和資料,避免出現Cache Stall。

Hekaton編譯查詢處理

Hekaton的編譯採用T-SQL儲存過程,編譯的中間形式叫做MAT Generator,生成最終的C程式碼在編譯器中執行。它產生的庫和通用Operator的區別在於:通用Operator需要在執行時解釋資料型別;而Hekaton編譯方式是把表的定義和查詢編譯在一起,每個庫只能處理對應的表而不能通用,自然就能拿到資料型別,這樣的實現能獲得3~4倍的效能提升。

HyPer和MemSQL編譯查詢處理

HyPer的編譯方式是把查詢樹按照Pipeline的分割點每段編譯。而MemSQL採用LLVM做編譯,把MPI語言編譯成程式碼。

下圖是一個對MemSQL效能的測試。沒有采用編譯執行時,MemSQL兩次執行相同查詢的時間都是0.05秒;如果採用編譯執行,第一次耗時0.08秒,但是再執行時耗時僅0.02秒。

— 其他記憶體資料庫系統—

除了之前提到的幾種記憶體資料庫外,還有其他一些著名的記憶體DBMS出現。

i) SolidDB: 誕生於1992年的混合型資料庫系統,同時具備基於磁碟和記憶體的最佳化引擎,使用VTRIE(Variable-length Trie)樹索引和悲觀鎖機制進行併發控制,透過Snapshot Checkpoints恢復。

ii) Oracle Times Ten 早期是惠普實驗室名為Smallbase的研究專案,在2005年被Oracle收購,現在多作為大型資料庫系統的前端記憶體加速引擎。Oracle Times Ten支援靈活部署,具有獨立的DBMS引擎和基於RDBMS的事務快取;在BI工作時支援Memory Repository,透過Locking進行併發控制;使用行級Latching處理寫衝突,採用Write-Ahead Logging和Checkpoint機制提高永續性。

iii) Altibase 於1999年在韓國成立,在電信、金融和製造業應用廣泛。Altibase在Page上儲存記錄,以Page為粒度進行Checkpoint且相容傳統DBMS引擎;支援多版本併發控制,使用預寫日誌記錄和檢查點實現永續性和恢復能力,透過Latching-Free對Page的資料進行Checkpoint。

iv) P*Time:  21世紀起源於韓國,2005年出售給SAP,現在是SAP HANA的一部分。P*Time具備極佳的效能處理,對日誌記錄使用差分編碼(XOR),採用混合儲存佈局,支援大於記憶體(Larger-than-Memory)的資料庫管理系統。

— 本文小結—

每一個資料庫系統都是針對特定的硬體環境設計,基於磁碟的資料庫系統面臨CPU單核、記憶體小、磁碟慢的場景設計。而記憶體資料庫以記憶體為主存,不需要再重複讀寫磁碟,磁碟I/O不再是效能瓶頸,而要解決其他瓶頸,比如:1. Locking/Latching的開銷;2. Cache-Line Miss,即如果資料結構定義的不夠好或在記憶體中組織的不好,無法匹配CPU的層級快取,會導致計算效能變差;3. Pointer Chasing,即需要另一個指標解釋,或者查另外的表才能查到記錄地址,也是主要的效能開銷。此外,還有Predicate Evaluation計算、資料遷移/儲存時的大量複製、分散式系統應用與資料庫系統的網路通訊等開銷。


在本專欄中,我們介紹了傳統基於磁碟的DBMS和記憶體資料庫的特點,並從資料組織、索引、併發控制、語句處理編譯、持久化幾個方面對記憶體資料庫與基於磁碟資料庫的相同和差異性進行了介紹:

1. 資料組織: 記憶體資料庫中,把記錄分成定長和變長管理,不需要資料連續儲存,用指標替代了Page ID + Offset的間接訪問;

2. 索引最佳化: 考慮面向記憶體中的Cach Line最佳化、快速的記憶體訪問等Latch-Free技術,以及索引的更新不記錄日誌等;

3. 併發控制: 可以採用悲觀和樂觀的併發控制方式,但是與傳統基於磁碟資料庫的區別是,記憶體資料庫鎖資訊和資料繫結,而不用單獨的Lock Table管理;

4. 查詢處理: 記憶體資料庫場景下的順序訪問和隨機訪問效能差別不大。可以透過編譯執行提高查詢效能;

5. 持久化: 仍然透過WAL(Write-Ahead Logging)做Logging,並採用輕量級的日誌,日誌記錄的內容儘量少,目的是降低日誌寫入磁碟延遲。

 

記憶體資料庫從1970s開始出現經歷了理論成熟、投入生產、市場驗證等發展環節。隨著當前網際網路秒殺、移動支付、短影片平臺等高併發、大流量、低時延的平臺出現,對於資料庫效能提出了巨大需求和挑戰。同時,記憶體本身在容量、單位價格友好度上不斷提升,以及近期非容失性儲存(NVM)的發展,促進了記憶體資料庫的發展,這些因素使得記憶體資料庫在未來有著廣闊的市場和落地機會。


注:本文相關內容參照以下資料:

1 . Pavlo, Andrew & Curino, Carlo & Zdonik, Stan. (2012). Skew-aware automatic database partitioning in shared-nothing, parallel OLTP systems. Proceedings of the ACM SIGMOD International Conference on Management of Data. DOI: 10.1145/2213836.2213844. 

2.  Kemper, Alfons & Neumann, Thomas. (2011). HyPer: A hybrid OLTP&OLAP main memory database system based on virtual memory snapshots. Proceedings - International Conference on Data Engineering. 195-206.  DOI: 10.1109/ICDE.2011.5767867. 

3. Faerber, Frans & Kemper, Alfons & Larson, Per-Åke & Levandoski, Justin & Neumann, Tjomas & Pavlo, Andrew. (2017). Main Memory Database Systems. Foundations and Trends in Databases. 8. 1-130. DOI: 10.1561/1900000058. 

4. Sikka, Vishal & Färber, Franz & Lehner, Wolfgang & Cha, Sang & Peh, Thomas & Bornhövd, Christof. (2012). Efficient Transaction Processing in SAP HANA Database –The End of a Column Store Myth. DOI: 10.1145/2213836.2213946. 

5. Diaconu, Cristian & Freedman, Craig & Ismert, Erik & Larson, Per-Åke & Mittal, Pravin & Stonecipher, Ryan & Verma, Nitin & Zwilling, Mike. (2013). Hekaton: SQL server's memory-optimized OLTP engine. 1243-1254. DOI: 10.1145/2463676.2463710. 


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

相關文章