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

星環科技發表於2021-01-29

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


8月26日,星環邀請來自華東師範大學軟體工程學院的博士生導師宮學慶教授帶來《資料庫前沿技術系列講座》,分享資料庫業內前沿發展和研究熱點。現將宮學慶教授的培訓第一講內容:記憶體資料庫的技術發展分享給大家

— 基於磁碟的資料庫管理系統  —

傳統的資料庫管理系統(DBMS)通常是採用基於磁碟的設計,原因在於早期資料庫管理系統設計時受到了硬體資源如單CPU、單核、可用記憶體小等條件的限制,把整個資料庫放到記憶體裡是不現實的,只能放在磁碟上。由於磁碟是一個非常慢的儲存裝置(相對於CPU的速度),因此學術界和工業界發展出的資料庫管理系統在架構上都必須適應當時的硬體條件,沿用至今的Oracle和MySQL等資料庫管理系統仍然採用的是這種架構設計。


伴隨著技術的發展,記憶體已經越來越便宜,容量也越來越大。單臺計算機的記憶體可以配置到幾百GB甚至TB級別。對於一個資料庫應用來說,這樣的記憶體配置已經足夠將所有的業務資料載入到記憶體中進行使用。雖然大資料處理的資料量可能是PB級別的,但那些資料一般是非結構化的資料。通常來講,結構化資料的規模並不會特別大,例如一個銀行10年到20年的交易資料加在一起可能只有幾十TB。這樣規模的結構化資料如果放在基於磁碟的DBMS中,在面對大規模SQL查詢和交易處理時,受限於磁碟的I/O效能,很多時候資料庫系統會成為整個應用系統的效能瓶頸。


如果我們為資料庫伺服器配置足夠大的記憶體,是否可以仍然採用原來的架構,透過把所有的結構化資料載入到記憶體緩衝區中,就可以解決資料庫系統的效能問題呢?這種方式雖然能夠在一定程度上提高資料庫系統的效能,但在日誌機制和更新資料落盤等方面仍然受限於磁碟的讀寫速度,遠沒有發揮出大記憶體系統的優勢。記憶體資料庫管理系統和傳統基於磁碟的資料庫管理系統在架構設計和記憶體使用方式上還是有著明顯的區別。

—  緩衝區管理方式  —

在傳統的資料庫管理系統中,資料的主儲存介質是磁碟。例如,邏輯上的一張表通常會被對映到磁碟上的一個檔案,檔案是以資料塊(Data Block,也稱作Page)的形式儲存在磁碟上。對於結構化資料來說,一條記錄會被儲存在磁碟上的某個資料塊中,可以用資料塊ID和Offset/偏移量來表示該條記錄的具體位置。這種形式的資料塊也被稱作 Slotted Page,顧名思義是把資料塊劃分成很多槽位,然後一個Record放在某一個槽位上。在對某條記錄進行處理時,可以透過代表該記錄地址的Page ID + Offset從磁碟上獲取該記錄;隨後系統會把儲存有該條記錄的資料塊從磁碟讀到緩衝區(Buffer Pool分為多個Frame,每個Frame可以儲存一個磁碟塊),再從緩衝區將該條記錄讀到執行緒或事務的工作區進行處理;處理結束後將更新的記錄寫回緩衝區中的資料塊,再由資料庫管理系統將修改過的資料塊寫回到磁碟上。

基於磁 盤的資料庫管理系 統中的資料訪問示例

在基於磁碟的資料庫管理系統中,處理查詢時通常會把整個索引載入到記憶體,而B+樹索引中一個索引節點的大小通常是一個資料塊。每個被索引的key值在索引葉子節點中都有對應的索引項,索引項中包含該key值所對應記錄的儲存位置(Page ID + Offset);當一個資料塊被載入到記憶體中的緩衝區時,DBMS透過Page Table結構來維護Page ID + Offset的地址與記憶體緩衝區地址的轉換。在訪問資料時,先在Page Table中查詢是否存在對應的Page ID + Offset,如果沒有則說明這條記錄仍然在磁碟上,需要先把磁碟上資料塊的讀進緩衝區,然後再在Page Table中維護好地址對映關係。具體的實現過程是,DBMS首先會在緩衝區中尋找可用的Frame,如果沒有就根據緩衝區替換演算法選取髒頁(Dirty Page)替換出去;假如選中了某個髒頁進行替換,則需要對該位置加Latch鎖來保證在替換過程中該位置不會被其他事務訪問(Latch後面會介紹)。在髒頁寫回磁碟後,系統就可以把目標資料塊讀入到緩衝區中的該位置,再將其在緩衝區中的地址寫到Page Table,維護好地址對映關係;在這些操作完成後再將Frame上的Latch鎖釋放。


傳統DBMS中的記憶體地址對映

對於傳統基於磁碟的DBMS而言,即使記憶體緩 衝區足夠大,可以將所有資料載入到記憶體中,但訪問資料過程中的地址對映和轉換依然存在,只是省掉了將資料塊從磁碟載入到記憶體的開銷。即使資料已經全部被載入到記憶體,基於磁碟的DBMS效能上與記憶體資料庫相比還是有很大差距,這是其中一個重要的原因。

 

總結來看,基於磁碟的DBMS和記憶體資料庫在實現技術上一個重要區別是:在訪問資料時,基於磁碟的DBMS需要透過地址對映將資料在磁碟上的地址轉換成在記憶體中地址,而記憶體資料庫在設計上則是直接使用資料在記憶體中的地址

  —  事務AC ID屬性保證 

在資料庫管理系統中,需要保證併發訪問場景下事務的ACID屬性,即事務的原子性、一致性、隔離性和永續性。事務的ACID屬性主要靠資料庫管理系統中的兩個機制實現,一個是併發控制,另一個是Logging/Recovery機制。

  • 併發控制

傳統基於磁碟的DBMS大部分是採用基於鎖(Lock)的悲觀併發控制,即事務在訪問資料時先加鎖,用完後再進行解鎖,其他事務在訪問資料時如果存在衝突則需要等待擁有鎖的事務釋放鎖。傳統DBMS一般會在記憶體中維護一個單獨資料結構——Lock Table來存放所有的鎖,由Lock Manager模組進行統一管理,這樣在記憶體中鎖和緩衝區中的資料是分開存放和管理的。事務在訪問資料時先向Lock Manager申請資料所對應的鎖,然後再訪問資料;執行結束後透過Lock Manager把鎖釋放,Lock Manager能夠保證所有事務申請和釋放鎖都是遵循嚴格的兩階段封鎖協議(strict 2 phase locking protocol)。同時,併發控制機制所帶來的開銷與使用者的實際業務處理沒有直接關係,是用於保證事務一致性和隔離性的額外開銷。

記憶體資料庫在訪問資料時也需要加鎖,但和基於磁碟的DBMS不同,鎖和資料在記憶體中是存放在一起的,通常是將鎖資訊儲存在資料記錄Header中。為什麼基於磁碟的DBMS要單獨將鎖資訊放在Lock Table中,而記憶體資料庫就可以把鎖資訊和資料存放在一起呢?因為在基於磁碟的DBMS中,資料塊是有可能被系統從記憶體緩衝區中替換到磁碟上,如果鎖資訊和資料放在一起,一旦資料塊被替換出去,Lock Manager和所有事務都無法獲得關於資料的鎖資訊。所以說對於傳統基於磁碟的DBMS來講,鎖要單獨維護在記憶體中,且需要始終保持在記憶體中,不能被替換出去。而對於記憶體資料庫來說,不存在這樣的場景。

 

實際上,資料庫管理系統中有兩種鎖機制,分別被稱為Lock和Latch,目的都是為了保護資料的一致性不被併發訪問所破壞。Lock機制是對資料庫邏輯內容的保護,一般來說擁有持續時間長,通常是事務執行的整個過程;並且Lock機制要支援事務的回滾以撤銷事務對資料修改。而Latch機制是為了保證記憶體中特定的資料結構不會因為併發訪問而導致錯誤,比如在多執行緒程式設計時有一個共享佇列發生插入、刪除等操作時,需要Latch保證操作過程中的佇列不受其他執行緒的干擾。Latch的保持時長與操作有關,本次操作做完就結束,同時也不需要支援對資料修改的回滾。


所以傳統DBMS如果要對緩衝區中的一個Page做操作則需要加Latch;如果是修改資料庫的內容則需要加Lock,單獨放在Lock Table維護和管理。下圖是對Lock和Latch的一個簡單對比

Lock和Latch特徵對比
  • Logging 和 Recovery

資料庫管理系統中,Logging和Recovery機制是日誌來保證事務的原子性和永續性的方式。原子性意味著一個事務中的所有操作必須同時成功或者撤銷,在執行一半做不下去時,可以按照日誌進行回滾;永續性意味著資料如果丟失,可以根據日誌來進行恢復。


在傳統 DBMS的L ogging和Recovery中,最重要的概念是WAL(Write-Ahead Log)——預寫式日誌。 WAL是指系統中所有更新操作都有對應的日誌,而在日誌沒有落盤前,對資料的修改不 允許落盤 系統中每條日誌都有一個LSN號(Log Sequence Number),所有的LSN號單調遞增,日誌落盤的過程是向磁碟的連續寫(順序寫)。但 如果系統嚴格按照一條日誌對應一條操作,日誌落盤後馬上將操作對資料的更新結果落盤,那麼系統效能會受到很大影響。所以,大多數的DBMS會採用 Steal + No Force的緩衝區管理策略。 Steal是指DBMS可以將未提交事務的更新刷到磁碟,不必等事務提交時再把更新刷到磁碟,提高了系統刷盤的靈活性和效能; 如果在事務未提交時發生crash,由於更新可能已經寫到磁碟,這時就需要透過對日誌的undo操作進行回滾。 No Force是指在事務已經提交後,對資料的更新可以依然存放在記憶體緩衝區中不寫入磁碟,在合併其他事務的更新後再一次性寫入磁碟,為系統提供最佳化空間。 但No Force可能帶來的風險是: 如果事務已經成功提交但更新沒有寫到磁碟,此時出現crash,則仍然在記憶體中的資料更新就會丟失,需要根據已經寫到磁碟的日誌(事務成功提交的前提是其所有日誌都必須已經落盤)進行redo操作。


有了WAL和Steal + No Force機制後,就可以給基於磁碟的DBMS提供最大的靈活性,來最佳化磁碟I/O。但對於記憶體資料庫而言,所有的資料放在記憶體裡,是否還需要這個機制呢?可以明確的一點是,記憶體資料庫還是需要Logging的,但和基於磁碟的DBMS有所區別,在日誌中只記載redo操作所需的資訊,不記載undo所需的資訊。 大家可以想一下這是為什麼?另一方面,記憶體資料庫在Logging過程中不記錄關於索引的更新,只記錄對於基礎表的更新,那Logging過程中所需寫盤的內容就少了很多。而在記憶體資料庫出現故障需要恢復時,首先從磁碟上儲存的檢查點(Check Point)資料和日誌中恢復基礎表,然後在記憶體中重新構造索引。

—  面向磁碟的DBMS效能開銷 

2008年,SIGMOD的一篇論文對面向磁碟的資料庫效能開銷做了分析,把整個資料庫系統的開銷做了劃分。分析發現:假設一次業務處理的總開銷是100%,實際上只有7%不到的資源是在真正處理業務邏輯;34%用於緩衝區管理如緩衝區的載入替換、地址轉化等;14%處理Latching;16%處理Locking;然後12%處理Logging;最後16%用於對B樹索引的處理。也就是說,機器資源跑滿負荷以後,真正用於處理業務邏輯的只有7%。

磁碟資料庫系統效能開銷

那麼是否可以將開銷大的部分去掉,來提高業務邏輯的資源佔比呢?如果資料庫是單使用者的,沒有併發競爭衝突,那麼可以省去Locking和Latching等方面的開銷。歷史上也有一些單執行緒的解決方案,例如將資料庫分成多個Partition,每個Partition由一個執行緒處理等。但這樣的方案具有明顯缺點:每個Partition是序列處理,假如有一個長的事務在執行,序列處理將導致後續事務全部被阻塞,直到該事務結束。而且面向磁碟的系統在進行大規模事務處理時瓶頸是磁碟I/O,如果單執行緒執行,在從磁碟讀取資料時CPU將處於空閒狀態。但對於記憶體資料庫來說,所有資料儲存在記憶體,磁碟I/O不是系統主要瓶頸,因此使用的技術與之前有了很大的差別。當然技術在發展過程中也經歷了各種各樣的嘗試,某些技術的發展不適合於現實背景,慢慢就被人忘記了。


可以看到,基於磁碟的資料庫管理系統做了很多額外的管理工作,這些工作雖然不處理業務邏輯,但在保證業務邏輯正確性上不可或缺。對於記憶體資料庫而言,面臨的問題是應該做哪些最佳化來得到最優的效能。和基於磁碟的系統相比,記憶體資料庫主儲存是記憶體,但依然需要磁碟來做Check Point和Logging,故障時要靠磁碟上的檢查點資料和日誌來恢復整個記憶體資料庫

  記憶體資料庫技術歷史發展 

記憶體資料庫的發展大致可以分成三個階段:1984年到1994年的10年;1994年到2005年的10年;2005年以後到現在。第一個階段出現了記憶體相關的處理技術;第二階段出現了一些記憶體資料庫系統;第三個階段就是我們現在面臨的場景。

  • 1984年 - 1994年

在1984年到1994年間,學術界針對記憶體資料管理提出了很多假設,比如記憶體緩衝區可以放進全部資料,可以採用組提交和快速提交最佳化技術等。同時也提出了面向記憶體的資料訪問方法,不再像基於磁碟的DBMS一樣採用Page ID + Offset方式進行訪問,而是在所有資料結構中都直接採用記憶體地址。還有面向記憶體的T-tree索引結構以及對系統按功能分成多個處理引擎,有的專門做事務處理,有的專門做恢復,相當於有兩個核,一個專門負責事務處理,另一個負責日誌處理。此外還有和Partition相關的主存資料庫,把資料庫分成很多個Partition,每個Partition對應一個核(或節點),程式間沒有競爭。可以看到,這個期間的資料庫技術發展已經在考慮如果資料全部放在記憶體,可以採用哪些技術。但受限於當時的硬體條件,這些技術並沒有得到大規模應用

  • 1994年 - 2005年

1994年到2005年間出現了一些商業記憶體資料庫系統,比如貝爾實驗室研發的Dali、Oracle Times Ten的前身Smallbase等。同時,也出現了一些面向多核的最佳化系統如P*-Time(現在是SAP-HANA事務 處理引擎)。當時也有一些Lock-f ree的實現技術被應用於記憶體資料庫系統,即無鎖的程式設計技術和資料結構。

  • 前兩階段小結

前兩個階段的技術大致可以分成這樣幾類:
1、解決Buffer Pool的In-Direction訪問: 把間接訪問替換掉,換成直接的記憶體地址訪問;索引的葉子節點不再放Page ID 和Offset,而直接是記憶體地址。
2、Data Partition: 切分資料,不做併發訪問控制的一類技術。
3、Lock-free和Cache-Conscious: 較於 面向磁碟的資料庫管理系統把一個索引節點儲存在一個資料塊中,記憶體資料庫中一個索引節點是一個或幾個Cache Line的長度。
4、粗粒度的鎖: 一次鎖一張表或一個Partition,而不是一條記錄,但這種技術現在使用較少,因為多核場景訪問競爭激烈,粗粒度鎖可能導致併發程度降低。(目前使用較少)
5、Functional Partition:把系統按照功能進行切分,每一個執行緒負責特定的功能等。 (目前 使用較少

DBMS歷史技術總結

— 資料庫系統的現代化發展 

在現在的環境中,硬體條件基本有三個特點:1. 記憶體大而便宜;2. 多核CPU(從主頻提升轉變到核心數的提升);3. Multi-Socket即多核多CPU,意味著處理的併發程度可以越來越高。這些都是資料庫系統研發在當下所面臨的情況。

現代硬體環境

對於 記憶體 資料庫而言,CPU和磁碟I/O不再是主要瓶頸,因此最佳化技術目前主要從以下角度來考慮:

  • 去掉傳統的緩衝區機制 傳統的緩衝區機制在記憶體資料庫中並不適用,鎖和資料不需要再分兩個地方儲存,但仍然需要併發控制,需要採用與傳統基於鎖的悲觀併發控制不同的併發控制策略。
  • 儘量減少執行時開銷: 磁碟I/O不再是瓶頸,新的瓶頸在於計算效能和功能呼叫等方面,需要提高執行時效能。
  • 採用編譯執行方式: 傳統資料庫多采用火山模型執行引擎,每一個Operator都被實現為一個迭代器,提供三個介面:Initial、Get-Next、Closed,從上往下依次呼叫。這種執行引擎的呼叫開銷在基於磁碟的資料庫管理系統中不佔主要比重(磁碟I/O是最主要瓶頸),但在記憶體資料庫裡可能會構成瓶頸。假設要讀取100萬條記錄,就需要呼叫100萬次,效能會變得難以忍受,這就是記憶體資料庫中大量採用編譯執行方式的原因。直接呼叫編譯後的機器程式碼,不再需要執行時的解釋和指標呼叫,效能會有效提升。
  • 可擴充套件的高效能索引構建: 雖然記憶體資料庫不從磁碟讀資料,但日誌依然要寫進磁碟,需要考慮日誌寫速度跟不上的問題。可以減少寫日誌的內容,例如把undo資訊去掉,只寫redo資訊;只寫資料但不寫索引更新。如果資料庫系統崩潰,從磁碟上載入資料後,可以採用併發的方式重新建立索引。只要基礎表在,索引就可以重建,在記憶體中重建索引的速度也比較快。

— 本文小結 

本篇主要介紹了基於磁碟的資料庫管理系統與記憶體資料庫管理系統在幾個實現方面存在的主要異同,以及記憶體資料庫從1984年開始到現在的技術發展。後面會繼續分享關於記憶體資料庫技術的發展,從資料組織、索引、併發控制、編譯查詢和持久化角度出發,介紹並對比幾款主流記憶體資料庫產品的實現技術。

 

:本文部分材料來自於:

1. VLDB 2016會議上的現代主存資料庫系統教程(Modern Main-Memory Database Systems Tutorial)

2. CMU(卡耐基梅隆大學)Andy Pavlo教授的高階資料庫系統(Advanced Database Systems)課程


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

相關文章