內存數據庫解析與主流產品對比(一)

做者: 實驗室小陳 / 大數據開放實驗室算法

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須要經過地址映射將數據在磁盤上的地址轉換成在內存中地址,而內存數據庫在設計上則是直接使用數據在內存中的地址。

 

 — 事務ACID屬性保證 

在數據庫管理系統中,須要保證併發訪問場景下事務的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的Logging和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-free的實現技術被應用於內存數據庫系統,即無鎖的編程技術和數據結構。

  • 前兩階段小結

前兩個階段的技術大體能夠分紅這樣幾類:

一、解決Buffer Pool的In-Direction訪問:把間接訪問替換掉,換成直接的內存地址訪問;索引的葉子節點再也不放Page ID 和Offset,而直接是內存地址。

二、Data Partition:切分數據,不作併發訪問控制的一類技術。

三、Lock-free和Cache-Conscious:相較於面向磁盤的數據庫管理系統把一個索引節點存儲在一個數據塊中,內存數據庫中一個索引節點是一個或幾個Cache Line的長度。

四、粗粒度的鎖:一次鎖一張表或一個Partition,而不是一條記錄,但這種技術如今使用較少,由於多核場景訪問競爭激烈,粗粒度鎖可能致使併發程度下降。(目前使用較少)

五、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)課程

 

相關文章
相關標籤/搜索