數據庫恢復子系統的常見技術和方案對比(二)

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

上一篇文章《數據庫恢復子系統的常見技術和方案對比(一)》中,咱們基本介紹了數據庫管理系統中的Logging & Recovery恢復子系統,詳細討論了基於Physical Logging的主流恢復算法ARIES的概念和技術實現。本文將華師大宮學慶教授關於介紹Logical Undo Logging 的原理以及兩種數據庫系統SQL Server(Azure)和Silo的恢復技術的介紹分享給你們。 數據庫

— Logical Undo Logging —

在上篇文章中,咱們簡單介紹了Early Lock Release的優化思路:經過將索引上的Lock提早釋放以提升併發程度,但同時會在事務之間產生依賴關係,致使級聯回滾。好比第一個事務已經釋放鎖,但在刷日誌時出現故障須要回滾,此時鎖已被下一個事務得到,那下一個事務要和前面的事務一塊兒回滾,極大地影響系統性能。架構

基於這樣的狀況,恢復系統中引入了Logical Undo Logging,在必定程度上解決了上述問題。Logical Undo Logging的基本思想是在事務回滾時撤銷修改操做,而不是對數據的修改,如插入的撤銷是刪除,對數據項+1的撤銷是對數據項-1。併發

此外處理Logical Undo時引入了Operation Logging的概念,即記錄日誌時對事務一些操做採用特殊的日誌記錄方式,稱爲Transaction Operation Logging。當Operation開始時,會記錄一條特殊的Operation Logging,形式爲log<Ti, Oj, operation-begin>,其中Oj爲惟一的Operation ID。在Operation執行過程當中,系統正常進行Physical Redo和Undo日誌記錄;而Operation結束後,則會記錄特殊的Logging <Ti, Oj,  operation-end, U>,其中U即是對於已經進行的一組操做的Logical Undo操做。app

舉例來講,若是索引插入新鍵值對(K5, RID 7)到葉子節點Index I9,其中K5存儲在Index I9頁面的X位置,替換原值Old1;RID7存儲在X+8位置上替換原值Old2。對於該事務,記錄日誌時會在最前面添加操做開始記錄<T1, O1, operation-begin>,中間Physical Logging正常記錄數值的更新操做,結束時記錄結束日誌<T1, O1, operation-end, (delete I9, K5, RID7)>,其中(delete I9, K5, RID7)是對T1操做的Logical Undo,即刪除I9節點頁面的K5和RID7。async

在Operation執行結束後,能夠提早釋放鎖,容許其餘事務能成功向該頁面插入< Key,Record ID>,但致使全部Key值從新排序,使得K5和RID7離開X和X+8的位置。此時若是進行Physical Undo,須要將K5和RID7從X 和X+8撤回,但實際中兩者的位置已發生改變,按原日誌記錄進行Physical Undo並不現實。但從邏輯上看,Undo只須要把K5和RID7從索引節點I9刪掉便可,所以在日誌中加入操做日誌(delete I9, K5, RID7),表示若是進行Undo,只須要按照這個邏輯指令進行便可。高併發

在回滾時,系統對日誌進行掃描:若是沒有operation-end的日誌記錄,則進行Physical Undo,這是由於只有在Operation結束時纔會釋放鎖,不然就說明鎖尚未被釋放,其餘事務不可能修改其鎖定的數據項。若是發現存在operation-end日誌,說明鎖已經被釋放掉,此時只能進行Logical Undo,把begin operation和end operation之間的全部日誌跳過,由於這些日誌的Undo已被Logical Undo替代。若是在Undo時發現了<T,O, operation-abort>日誌,則說明這個Operation已經Abort成功,能夠直接跳過中間日誌,回到該事務的<T, O,operation-begin>繼續往上Undo;當Undo到<T,start>的日誌時,代表該事務的所有日誌均已經撤銷完成,記錄<T,  abort>日誌表示Undo結束。性能

其中須要注意的是,全部Redo操做都是Physical Redo,這是由於Logical Redo的實現很是複雜,例如須要決定Redo順序等,所以大多數系統裏全部的Redo信息都是Physical的。此外,Physical Redo和鎖提早釋放並不衝突,由於Redo只有在出現故障時纔會進行,此時系統掛掉致使全部的鎖已經都沒有了,重作的時候要從新申請鎖。fetch

 

—SQL Server:Constant Time Recovery —

 

對於大多數商業的數據庫系統如SQL Server等,大多采用ARIES恢復系統,所以Undo全部未Commit事務的工做量與每一個事務所作的工做內容成正比。事務作的操做越多,Undo所花費的時間就越長。由於事務操做多是一條語句但更新不少記錄,Undo就須要對記錄逐條撤銷,致使撤銷時間可能過長。雖然正確性獲得保證,但對於雲服務或者對可用性要求高的系統將難以接受。爲了應對這種狀況,出現了CTR優化技術(Constant Time Recovery),將ARIES系統和多版本併發控制相結合,實現固定時間恢復。固定時間恢復是指無論遇到什麼狀況,都利用數據庫系統中的多版本信息,保證恢復操做在肯定的時間內完成。其基本思想是利用多版本數據庫系統中的不一樣數據版本,確保數據Undo到一個正確的狀態,而不是用原來WAL日誌裏的信息進行恢復。大數據

  • MS-SQL的併發控制

SQL Server在05年開始引入了多版本併發控制,但早期多版本僅用來實現Snapshot隔離級別而非恢復,支持系統根據Snapshot的時間戳去讀數據庫中的數據。在更新數據時,多版本併發控制能夠在數據Page上對記錄進行就地更新,但舊版本沒有丟掉,而是被單獨放在Version Store中。Version Store是一個特殊的表,只容許不斷的添加數據(Append-Only),經過指針連接指出該記錄的老版本,老版本又會指向上一個更老的版本,進而構成一個數據版本鏈。在訪問數據時,能夠根據事務的時間戳來決定讀取哪一個版本數據。所以,早期多版本Version Store的更新並不須要記錄日誌,由於一旦出現故障,重啓之後全部時間戳都是最新的,只要保持最後一個版本便可,不會有比這個版本更早的Snapshot訪問需求。對於當前Active的事務,能夠根據當下時間戳,經過Garbage Collection機制將老版本丟棄。

CTR基於原Version Store進行優化,實現了Persistent Version Store,使得舊版本可以持久化存儲。在CTR技術下,系統更新Version Store時會記錄日誌爲恢復進行的準備,使得Version Store的體量和開銷變大,因而出現兩種實現老版本存儲的策略In-Row Versioning和Off-RowVersioning。

In-Row Versioning是指對於更新一條記錄的操做,若是隻是改動很小的數據量,就不須要放進Version Store存儲,而是在記錄後加一個delta值,說明屬性的數值變化。其目的是爲了下降Versioning時的開銷,由於同一個位置改動的磁盤I/O相對較小。

Off-Row Versioning則有一個特殊的系統表,用來存儲全部表的老版本,經過WAL記錄Insert操做的Redo記錄。當修改量較大致使In-Row Versioning沒法徹底保存數據更新時,便採用Off-Row Versioning方式。以下圖中,A4行的Col 2爲444,在更新爲555後會寫入一個delta,用於記錄版本變化。但這種修改受限於數據量的大小,以及記錄自己所在的頁面上是否有空閒空間。若是有空閒空間就能夠寫入,若沒有就須要把更新的記錄放入Off-Row Versioning表中。

 

恢復過程當中,CTR分爲三個階段實現(Analytics、Redo和Undo)。分析階段和ARIES相似,用來肯定每個事務的當下狀態如Active、Commit和須要Undo的事務。在Redo時,系統會把主表和Version Store的表重放一遍,都恢復到出現crash的狀態。Redo完成後,數據庫就能夠上線對外服務。CTR的第三步是Undo,在分析階段結束後,已經知道哪些事務未提交,Undo階段能夠直接將這些事務標記爲Abort。因爲每條Record的不一樣版本都會記錄與該版本相關的事務號,所以後續事務在讀到該版本時,首先判斷相關事務的狀態,若是是Abort就忽略掉該版本而讀上一個舊版本。這樣的恢復方式使得在讀到不可用版本時,須要根據連接去找前一個版本,雖然會帶來額外性能開銷,但減小了數據庫的下線時間。在繼續提供服務後,系統能夠在剩下時間進行Garbage Collection,將失效老版本慢慢清除,這樣的機制叫作Logical Revert。

  • Logical Revert

Logical Revert有兩種方式。第一種是用後臺進程Background Cleanup把全部數據塊掃描一遍,判斷哪些是Garbage能夠回收。判斷條件爲:若是主表中最後一個版原本自於已經Abort的事務,那就從Version Store裏拿上一個已經Commit的舊版本放到主表。即便此時不作這件事情,後面使用數據時也是會到Version Store中讀數據。所以,能夠經過後臺的Garage Collection進程,慢慢進行版本搬遷。第二種是若是事務在更新數據時,發現主表裏的版本是Abort的事務的版本,就會覆蓋該版本,而此時這個事務的正確版本應該在Version Store中。

能夠看到,CTR的恢復是一個固定時間,只要前兩個階段結束便可,而前兩個階段所需時間實際上只與事務的Checkpoint有關。若是作Checkpoint的間隔是按照固定的日誌大小決定,當Redo階段結束,數據庫即可以恢復工做,且恢復時間不會超過一個固定值。

—Soil : Force Recovery—

Silo是哈佛大學和MIT合做研究的一個高性能內存數據庫系統原型,解決了併發程度太高致使的吞吐率降低問題。若是一個CPU內核對應一個線程來執行事務,在不存在競爭的狀況下,吞吐率隨着核數增長而增長,但高到必定程度後會出現降低,可能的狀況是由於出現了某些資源競爭所致使的瓶頸。儘管在事務執行過程當中每一個線程單獨執行,但最終全部事務在提交前都須要拿到事務ID,事務ID是全局範圍的,事務經過原子性操做atomic_fetch_and_add(&global_tid)得到commit時的ID。而事務ID的分配經過全局的Manager角色實現,在事務申請ID時,Manager會經過計數+1來更新事務計數器,保證事務ID的全局惟一且遞增,所以Manager寫操做的速度會是系統性能的上限。當併發愈來愈高且事務都去申請ID時,就會出現競爭關係使得等待時間變長,致使吞吐率降低。

  • 樂觀的併發控制

對於性能瓶頸問題,Silo的解決思路是多核併發工做+共享內存的數據庫裏採用樂觀併發控制。樂觀併發控制在《內存數據庫解析與主流產品對比(三)》中介紹過,指事務在執行時認爲相互之間沒有任何影響,僅在提交時檢查是否存在衝突,若是沒有衝突再去申請全局的事務ID完成Commit。而Silo經過設計Force Recovery取消了所需的全局事務ID,使用Group Commit的概念,將時間分紅多個Epoch,每一個Epoch爲40毫秒。Epoch包含了當前時間段涉及的多個事務,所以能夠經過提交Epoch的方式將這一組事務一塊兒提交,不須要爲每一個事務逐一申請全局事務ID。但這種設計的缺陷在於若是事務執行時間超過40毫秒,帶來的跨級會對提交和恢復帶來影響。

在Silo中,每一個事務由Sequence Number + Epoch Number來區分,Sequence Number用來決定執行過程當中事務的順序,經過Sequence Number和Epoch Number共同決定恢復策略。每一個事務會有事務ID(TID),事務按照Epoch來進行Group Commit,總體的提交按照Epoch Number的前後實現序列化。事務ID爲64位,由狀態位、Sequence Number和Epoch Number共同組成,其中高位是Epoch Number,中間是Sequence Number,前三位是狀態位。每條記錄都會保存對應的事務ID,其中狀態位用來存放訪問記錄時的Latch鎖等信息,內存數據庫和傳統基於磁盤的DBMS數據庫相比,其中一個重要區別就是鎖的管理是和記錄放在一塊兒,並不會另外管理Data Buffer和Locking Table。

 

  • 事務提交的三個階段

因爲Silo採用標準的樂觀併發控制,所以只有在提交時纔會檢查是否存在衝突。在pre-commit階段,讀數據項時會把數據項中的事務ID存入Local Read-Set,隨後再讀取數值;而在修改數據記錄時,也須要把修改的數據記錄放入Local Write-Set裏。

Silo事務提交時分三個階段,第一步爲Local Wite-Set裏全部要寫的記錄拿到鎖,鎖信息保存在事務ID中的狀態位,經過原子操做Compare and Set獲取;隨後從當前的Epoch讀出所有事務。系統裏有專門線程負責更新Epoch(每40ms+1),全部事務不會去競爭寫Epoch Number而只要讀該值便可。事務提交的第二步是檢查Read-Set,Silo中每一個數據項都包含最後一個對其進行更新的事務的ID,若是記錄的TID發生變化或記錄被其餘事務鎖住,說明從讀到提交的過程當中記錄已經更改,須要進行Rollback。最後是生成事務TID,在新事務Commit時,TID中的Sequence Number應該是一個大於全部讀到的Record的事務ID的最小值,以保證事務遞增。提交後只有所有事務落盤後,纔會返回結果

  • Recovery——SiloR

SiloR是Silo的恢復子系統,使用Physical Logging和Checkpoints來保證事務的持久性,並在這兩個方面採用並行恢復策略。前面提到,內存數據庫中的寫日誌操做速度最慢,由於日誌涉及寫盤,磁盤I/O是整個系統架構的性能瓶頸。所以SiloR採用併發方式寫日誌,並存在如下假設:系統裏每一個磁盤有一個日誌線程,用來服務一組工做線程,日誌線程和一組工做線程共享一個CPU Socket。

基於此假設,日誌線程須要維護日誌緩衝區池(池中有多個日誌緩衝區)。一個工做線程若是要執行,首先須要向日志線程索要一個日誌緩衝區寫日誌,寫滿後交還日誌線程落盤,同時再得到新的日誌緩衝區;若是沒有可用的日誌緩衝區,工做線程便會阻塞。日誌線程會定時將緩衝區刷到磁盤,緩衝區空出來後,能夠繼續交給工做線程處理。對於日誌文件,每100個Epoch生成1個新日誌文件,舊日誌文件按照固定規則生成文件名,文件名最後部分用來標識日誌中最大的Epoch Number;日誌內容則記錄了事務的TID和Record更新的集合(Table, Key, Old Value -> New Value)

 

上面介紹了多核CPU中一個核所處理的事情,實際上CPU中每一個核都以一樣的方式工做,會有一個專用線程來追蹤目前哪些日誌已經所有刷到磁盤上,並把最新已經落盤的Epoch寫到磁盤上的固定位置。全部事務經過比較本身當前的Epoch和已經落盤的Persistent Epoch(如下簡稱Pepoch),若是小於等於 Pepoch,代表日誌已經落盤,能夠向客戶端返回。

  • 恢復過程

和ARIES恢復系統同樣,SlioR也要作Checkpoints。SlioR的第一步是把最後一個檢查點的數據讀出再進行恢復;因爲內存數據庫不對索引作日誌,所以索引須要在內存中重構。第二步是日誌回放,內存數據庫只作Redo不作Undo,Redo操做並非和ARIES同樣按正常日誌順序進行,而是從後向前執行直接更新到最新版本。在日誌回放時,先檢查Pepoch的日誌文件,找出最新的Pepoch號,凡是超過該Pepoch號的日誌記錄,則認爲對應的事務沒有返回給客戶端,所以能夠忽略。日誌文件的恢復採用Value Logging,對於每條日誌,檢查數據的Record是否已經存在,若是不存在就根據日誌記錄生成數據Record;若是存在則比較Record和日誌中的TID。若是日誌中的TID大於Record中的TID,就證實須要Redo,用日誌中的新數據值來替換舊值。能夠看到,SiloR的Redo不是像ARIES同樣一步步恢復到故障現場,由於ARIES的目的是要將磁盤上的數據恢復到最終正確狀態,而SiloR是要把內存當中的數據恢復到最新正確狀態。

—In-Memory Checkpoint—

對於內存數據庫,恢復系統相比磁盤DBMS系統較爲簡單些,只要對數據作日誌便可,索引則不須要,且只需Redo日誌而不需Undo日誌。全部數據都是直接覆蓋修改,不須要管理Dirty Page,不會存在Buffer Pool和緩衝區落盤問題。但內存數據庫仍受限於日誌同步時間開銷,由於日誌依舊要刷到非易失性存儲(磁盤)。早期80年代時,內存數據庫的研究都是假設內存數據不會丟失,如帶電池的內存(斷電後還能夠用電池支撐一段時間工做);還有非易失性內存NVM(Non-Volatile Memory)等技術,但目前這些技術還遠達不到廣泛使用,依舊要考慮持久化存儲。

持久化系統要求對性能影響最小,不能影響到吞吐量和延遲。爲減小對事務執行的影響,內存數據庫追求的第一目標是恢復速度,即在故障後能夠最快時間內完成恢復。所以串行恢復沒法知足速度要求,目前有不少研究工做都集中在對內存數據庫的並行日誌研究,而並行使得對於鎖和Checkpoints的實現都變得複雜。

對於In-Memory的Checkpoints,實現機制一般和併發控制緊密融合,併發控制設計決定了檢查點如何實現。理想的In-Memory中Checkpoints第一個要求是不能影響正常的事務執行,而且不能引入額外延遲以及佔用太多內存。

Checkpoints種類:Checkpoints分爲一致性Checkpoints和Fuzzy Checkpoints兩類。一致性Checkpoints指產生的檢查點中的數據不包含任何未提交的事務,若是包含則在Checkpoints時去掉未提交的事務;Fuzzy Checkpoints包含已經提交和未提交的事務,在恢復時才把未提交的事務去掉。

Checkpoints機制:Checkpoints機制分爲兩種,第一種能夠經過開發數據庫系統自身功能來實現Checkpoint,好比使用多版本存儲來作Snapshot;第二種能夠經過操做系統級別的Folk功能,拷貝出進程的子進程;隨後把內存中全部數據拷貝一遍,但須要額外操做去回滾正在執行且還未提交的修改。

Checkpoints內容:內容上Checkpoint分兩種類型,一種是每次Checkpoint都全量拷貝數據;還有一種是增量Checkpoint,每次只作本次與上一次之間產生的增量內容。兩者差別在於Checkpoint時數據量以及恢復時所須要的數據量的區別。

Checkpoints頻率:有三種Checkpoint頻率。一是基於時間的定週期Checkpoint;二是基於固定日誌的大小的Checkpoint;最後一種是強制性Checkpoint,如數據庫下線後強制進行Checkpoint

—本文小結—

在本次兩篇文章專欄中,咱們對數據庫系統的恢復子系統進行了介紹,分別介紹了Physical Logging的主流恢復系統ARIES和Logical Undo Logging的相關概念和技術實現。此外,咱們介紹了兩個數據庫系統的恢復策略——SQL Server的CTR固定時間恢復以及內存數據庫系統Silo的Force Recovery恢復原理。下一講將討論數據庫系統的併發控制技術。

 

參考文獻:

1. C. Mohan, Don Haderle, Bruce Lindsay, Hamid Pirahesh, and Peter Schwarz. 1992. ARIES: A Transaction Recovery Method Supporting Fine-Granularity Locking And Partial Rollbacks Using Write-Ahead Logging. ACM Trans. Database Syst. 17, 1 (March 1992), 94–162. 

2. Antonopoulos, P., Byrne, P., Chen, W., Diaconu, C., Kodandaramaih, R. T., Kodavalla, H., ... & Venkataramanappa, G. M. (2019). Constant time recovery in Azure SQL database. Proceedings of the VLDB Endowment, 12(12), 2143-2154.

3. Zheng, W., Tu, S., Kohler, E., & Liskov, B. (2014). Fast databases with fast durability and recovery through multicore parallelism. In 11th {USENIX} Symposium on Operating Systems Design and Implementation ({OSDI} 14) (pp. 465-477).

4. Ren, K., Diamond, T., Abadi, D. J., & Thomson, A. (2016, June). Low-overhead asynchronous checkpointing in main-memory database systems. In Proceedings of the 2016 International Conference on Management of Data (pp. 1539-1551).

5. Kemper, A., & Neumann, T. (2011, April). HyPer: A hybrid OLTP&OLAP main memory database system based on virtual memory snapshots. In 2011 IEEE 27th International Conference on Data Engineering (pp. 195-206). IEEE.

相關文章
相關標籤/搜索