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

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

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


上一篇文章《 資料庫恢復子系統的常見技術和方案對比(一) 》中,我們基本介紹了資料庫管理系統中的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操作。

舉例來說,如果索引插入新鍵值對(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。


在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只有在出現故障時才會進行,此時系統掛掉導致所有的鎖已經都沒有了,重做的時候要重新申請鎖。

— 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階段結束,資料庫便可以恢復工作,且恢復時間不會超過一個固定值。

—  Silo: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.



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

相關文章