資料庫原理 - 序列4 - 事務是如何實現的? - Redo Log解析(續)

来源:https://www.cnblogs.com/travis2046/archive/2019/04/12/10695955.html
-Advertisement-
Play Games

> 本文節選自《軟體架構設計:大型網站技術架構與業務架構融合之道》第6.4章節。 作者微信公眾號:> 架構之道與術。進入後,可以加入書友群,與作者和其他讀者進行深入討論。也可以在京東、天貓上購買紙質書。 ## 6.5.5 Redo Log Block結構 Log Block還需要有Check sum ...


> 本文節選自《軟體架構設計:大型網站技術架構與業務架構融合之道》第6.4章節。 作者微信公眾號:
> 架構之道與術。進入後,可以加入書友群,與作者和其他讀者進行深入討論。也可以在京東、天貓上購買紙質書。

## 6.5.5 Redo Log Block結構

Log Block還需要有Check sum的欄位,另外還有一些頭部欄位。事務可大可小,可能一個Block存不下產生的日誌數據,也可能一個Block能存下多個事務的數據。所以在Block裡面,得有欄位記錄這種偏移量。
圖6-9展示了一個Redo Log Block的詳細結構,頭部有12位元組,尾部Check sum有4個位元組,所以實際一個Block能存的日誌數據只有496位元組。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20190412103606585.?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NodW5sb25neXU=,size_16,color_FFFFFF,t_70)
圖6-9 Redo Log Block詳細結構
頭部4個欄位的含義分別如下:
Block No:每個Block的唯一編號,可以由LSN換算得到。
Date Len:該Block中實際日誌數據的大小,可能496位元組沒有存滿。
First Rec Group:該Block中第一條日誌的起始位置,可能因為上一條日誌很大,上一個Block沒有存下,日誌的部分數據到了當前的Block。如果First Rec Group = Data Len,則說明上一條日誌太大,大到橫跨了上一個Block、當前Block、下一個Block,當前Block中沒有新日誌。
Checkpoint No:當前Block進行Check point時對應的LSN(下文會專門講Checkpoint)。

## 6.5.6 事務、LSN與Log Block的關係

知道了Redo Log的結構,下麵從一個事務的提交開始分析,看事務和對應的Redo Log之間的關聯關係。假設有一個事務,偽代碼如下:

start transaction
update 表1某行記錄
delete 表1某行記錄
insert 表2某行記錄
commit

其產生的日誌,如圖6-10所示。應用層所說的事務都是“邏輯事務”,具體到底層實現,是“物理事務”,也叫作Mini Transaction(Mtr)。在邏輯層面,事務是三條SQL語句,涉及兩張表;在物理層面,可能是修改了兩個Page(當然也可能是四個Page,五個Page……),每個Page的修改對應一個Mtr。每個Mtr產生一部分日誌,生成一個LSN。
這個“邏輯事務”產生了兩段日誌和兩個LSN。分別存儲到Redo Log的Block里,這兩段日誌可能是連續的,也可能是不連續的(中間插入的有其他事務的日誌)。所以,在實際磁碟上面,一個邏輯事務對應的日誌不是連續的,但一個物理事務(Mtr)對應的日誌一定是連續的(即使橫跨多個Block)。
圖6-11展示了兩個邏輯事務,其對應的Redo Log在磁碟上的排列示意圖。可以看到,LSN是單調遞增的,但是兩個事務對應的日誌是交叉排列的。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2019041210372321.?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NodW5sb25neXU=,size_16,color_FFFFFF,t_70)
圖6-10 事務與產生的Redo Log對應關係
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20190412103753967.?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NodW5sb25neXU=,size_16,color_FFFFFF,t_70)
圖6-11 兩個邏輯事務的Redo Log在磁碟上排列示意圖

同一個事務的多條LSN日誌也會通過鏈表串聯,最終數據結構類似表6-9。其中,TxID是InnoDB為每個事務分配的一個唯一的ID,是一個單調遞增的整數。

表6-9 Redo Log與LSN和事務的關係
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2019041210384922.)

## 6.5.7 事務Rollback與崩潰恢復(ARIES演算法)

**1.未提交事務的日誌也在Redo Log中**
通過上面的分析,可以看到不同事務的日誌在Redo Log中是交叉存在的,這意味著未提交的事務也在Redo Log中!因為日誌是交叉存在的,沒有辦法把已提交事務的日誌和未提交事務的日誌分開,或者說前者刷到磁碟的Redo Log上面,後者不刷。比如圖6-11的場景,邏輯事務1提交了,要把邏輯事務1的Redo Log刷到磁碟上,但中間夾雜的有邏輯事務2的部分Redo Log,邏輯事務2此時還沒有提交,但其日誌會被“連帶”地刷到磁碟上。
所以這是ARIES演算法的一個關鍵點,不管事務有沒有提交,其日誌都會被記錄到Redo Log上。當崩潰後再恢復的時候,會把Redo Log全部重放一遍,提交的事務和未提交的事務,都被重放了,從而讓資料庫“原封不動”地回到宕機之前的狀態,這叫Repeating History。
重放完成後,再把宕機之前未完成的事務找出來。這就有個問題,怎麼把宕機之前未完成的事務全部找出來?這點講Checkpoint時會詳細介紹。
把未完成的事務找出來後,逐一利用Undo Log回滾。

**2.Rollback轉化為Commit**
回滾是把未提交事務的Redo Log刪了嗎?顯然不是。在這裡用了一個巧妙的轉化方法,把回滾轉化成為提交。
如圖6-12所示,客戶端提交了Rollback,資料庫並沒有更改之前的數據,而是以相反的方向生成了三個新的SQL語句,然後Commit,所以是邏輯層面上的回滾,而不是物理層面的回滾。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20190412103920589.)
圖6-12 一個Rollback事務被轉換為Commit事務示意圖
同樣,如果宕機時一個事務執行了一半,在重啟、回滾的時候,也並不是刪除之前的部分,而是以相反的操作把這個事務“補齊”,然後Commit,如圖6-13所示。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20190412103941287.)
圖6-13 宕機未完成的事務被轉換成Commit事務
這樣一來,事務的回滾就變得簡單了,不需要改之前的數據,也不需要改Redo Log。相當於沒有了回滾,全部都是Commit。對於Redo Log來說,就是不斷地append。這種逆向操作的SQL語句對應到Redo Log裡面,叫作Compensation Log Record(CLR),會和正常操作的SQL的Log區分開。

**3.ARIES恢復演算法**
如圖6-14所示,有T0~T5共6個事務,每個事務所在的線段代表了在Redo Log中的起始和終止位置。發生宕機時,T0、T1、T2已經完成,T3、T4、T5還在進行中,所以回滾的時候,要回滾T3、T4、T5。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20190412104046839.?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NodW5sb25neXU=,size_16,color_FFFFFF,t_70)
圖6-14 ARIES演算法示意圖
ARIES演算法分為三個階段:
**(1)階段1:分析階段**
分析階段,要解決兩個核心問題。
第一,確定哪些數據頁是臟頁,為階段2的Redo做準備。發生宕機時,雖然T0、T1、T2已經提交了,但只是Redo Log在磁碟上,其對應的數據Page是否已經刷到磁碟上不得而知。如何找出從Checkpoint到Crash之前,所有未刷盤的Page呢?
第二,確定哪些事務未提交,為階段3的Undo做準備。未提交事務的日誌也寫入了Redo Log。對應到此圖,就是T3、T4、T5的部分日誌也在Redo Log中。如何判斷出T3、T4、T5未提交,然後對其回滾呢?
這就要談到ARIES的Checkpoint機制。Checkpoint是每隔一段時間對記憶體中的數據拍一個“快照”,或者說把記憶體中的數據“一次性”地刷到磁碟上去。但實際上這做不到!因為在把記憶體中所有的臟頁往磁碟上刷的時候,資料庫還在不斷地接受客戶端的請求,這些臟頁一直在更新。除非把系統阻塞住,不再接受前端的請求,這時Redo Log也不再增長,然後一次性把所有的臟頁刷到磁碟中,叫作Sharp Checkpoint。
Sharp Checkpoint的應用場景很狹窄,因為系統不可能停下來,所以用的更多的是Fuzzy Checkpoint,具體怎麼做呢?
在記憶體中,維護了兩個關鍵的表:活躍事務表(表6-10)和臟頁表(表6-11)。
活躍事務表是當前所有未提交事務的集合,每個事務維護了一個關鍵變數lastLSN,是該事務產生的日誌中最後一條日誌的LSN。
表6-10 活躍事務表
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20190412104124110.)

臟頁表是當前所有未刷到磁碟上的Page的集合(包括了已提交的事務和未提交的事務),recoveryLSN是導致該Page為臟頁的最早的LSN。比如一個Page本來是clean的(記憶體和磁碟上數據一致),然後事務1修改了它,對應的LSN是LSN1;之後事務2、事務3又修改了它,對應的LSN分別是LSN2、LSN3,這裡recoveryLSN取的就是LSN1。
表6-11 臟頁表
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20190412104203359.)
所謂的Fuzzy Checkpoint,就是對這兩個關鍵表做了一個Checkpoint,而不是對數據本身做Checkpoint。這點非常巧妙!因為Page本身很多、數據量大,但這兩個表記錄的全是ID,數據量很小,很容易備份。
所以,每一次Fuzzy Checkpoint,就把兩個表的數據生成一個快照,形成一條Checkpoint日誌,記入Redo Log。
基於這兩個關鍵表,可以求取兩個問題:

問題(1):求取Crash的時候,未提交事務的集合。
以圖6-14為例,在最近的一次Checkpoint 2時候,未提交事務集合是{T2,T3},此時還沒有T4、T5。從此處開始,遍歷Redo Log到末尾。
在遍歷的過程中,首先遇到了T2的結束標識,把T2從集合中移除,剩下{T3};
之後遇到了事務T4的開始標識,把T4加入集合,集合變為{T3,T4};
之後遇到了事務T5的開始標識,把T5加入集合,集合變為{T3,T4,T5}。
最終直到末尾,沒有遇到{T3,T4,T5}的結束標識,所以未提交事務是{T3,T4,T5}。
圖6-15展示了事務的開始標識、結束標識以及Checkpoint在Redo Log中的排列位置。其中的S表示Start transaction,事務開始的日誌記錄;C表示Commit,事務結束的日誌記錄。每隔一段時間,做一次Checkpoint,會插入一條Checkpoint日誌。Checkpoint日誌記錄了Checkpoint時所對應的活躍事務的列表和臟頁列表(臟頁列表在圖中未展示)。

問題(2):求取Crash的時候,所有未刷盤的臟頁集合。
假設在Checkpoint2的時候,臟頁的集合是{P1,P2}。從Checkpoint開始,一直遍歷到Redo Log末尾,一旦遇到Redo Log操作的是新的Page,就把它加入臟頁集合,最終結果可能是{P1,P2,P3,P4}。
這裡有個關鍵點:從Checkpoint2到Crash,這個集合會只增不減。可能P1、P2在Checkpoint之後已經不是臟頁了,但把它認為是臟頁也沒關係,因為Redo Log是冪等的。
圖6-15 事務在Redo Log上排列示意圖

階段2:進行Redo
假設最後求出來的臟頁集合是{P1,P2,P3,P4,P5}。在這個集合中,可能都是真的臟頁,也可能是已經刷盤了。取集合中所有臟頁的recoveryLSN的最小值,得到firstLSN。從firstLSN遍歷Redo Log到末尾,把每條Redo Log對應的Page全部重刷一次磁碟。
關鍵是如何做冪等?磁碟上的每個Page有一個關鍵欄位——pageLSN。這個LSN記錄的是這個Page刷盤時最後一次修改它的日誌對應的LSN。如果重放日誌的時候,日誌的LSN <= pageLSN,則不修改日誌對應的Page,略過此條日誌。
如圖6-16所示,Page1被多個事務先後修改了三次,在Redo Log的時間線上,分別對應的日誌的LSN為600、900、1000。當前在記憶體中,Page1的pageLSN = 1000(最新的值),因為還沒來得及刷盤,所以磁碟中Page1的pageLSN = 900(上一次的值)。現在,宕機重啟,從LSN=600的地方開始重放,從磁碟上讀出來pageLSN = 900,所以前兩條日誌會直接過濾掉,只有LSN = 1000的這條日誌對應的修改操作,會被作用到Page1中。

圖6-16 pageLSN實現Redo Log冪等示意圖
這點與TCP在接收端對數據包的判重有異曲同工之妙!在TCP中,是對發送的數據包從小到大編號(seq number),這裡是對所有日誌從小到大編號(LSN),接收的一方發現收到的日誌編號比之前的還要小,就說明不用重做了。
有了這種判重機制,我們就實現了Redo Log重放時的冪等。從而可以從firstLSN開始,將所有日誌全部重放一遍,這裡麵包含了已提交事務和未提交事務的日誌,也包含對應的臟頁或者乾凈的頁。
Redo完成後,就保證了所有的臟頁都成功地寫入到了磁碟,乾凈頁也可能重新寫入了一次。並且未提交事務T3、T4、T5對應的Page數據也寫入了磁碟。接下來,就是要對T3、T4、T5回滾。

階段3:進行Undo
在階段1,我們已經找出了未提交事務集合{T3,T4,T5}。從最後一條日誌逆向遍歷,因為每條日誌都有一個prevLSN欄位,所以可以沿著T3、T4、T5各自的日誌鏈一直回溯,最終直到T3的第一條日誌。
所謂的Undo,是指每遇到一條屬於T3、T4、T5的Log,就生成一條逆向的SQL語句來執行,其執行對應的Redo Log是Compensation Log Record(CLR),會在Redo Log尾部繼續追加。所以對於Redo Log來說,其實不存在所謂的“回滾”,全部是正向的Commit,日誌只會追加,不會執行“物理截斷”之類的操作。
要生成逆向的SQL語句,需要記錄對應的歷史版本數據,這點將在分析Undo Log的時候詳細解釋。
這裡要註意的是:Redo的起點位置和Undo的起點位置並沒有必然的先後關係,圖中畫的是Undo的起點位置小於Redo的起點位置,但實際也可以反過來。以為Redo對應的是所有臟頁的最小LSN,Undo對應的是所有未提交事務的起始LSN,兩者不是同一個維度的概念。
在進行Undo操作的時候,還可能會遇到一個問題,回滾到一半,宕機,重啟,再回滾,要進行“回滾的回滾”。
如圖6-17所示,假設要回滾一個未提交的事務T,其有三條日誌LSN分別為600、900、1000。第一次宕機重啟,首先對LSN=1000進行回滾,生成對應的LSN=1200的日誌,這條日誌里會有一個欄位叫作UndoNxtLSN,記錄的是其對應的被回滾的日誌的前一條日誌,即UndoNxtLSN = 900。這樣當再一次宕機重啟時,遇到LSN=1200的CLR,首先會忽略這條日誌;然後看到UndoNxtLSN = 900,會定位到LSN=900的日誌,為其生成對應的CLR日誌LSN=1600;然後繼續回滾,LSN=1700的日誌,回滾的是LSN=600。
這樣,不管出現幾次宕機,重啟後最終都能保證回滾日誌和之前的日誌一一對應,不會出現“回滾嵌套”問題。
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20190412104354375.?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NodW5sb25neXU=,size_16,color_FFFFFF,t_70)
圖6-17 回滾過程中出現宕機後再次重啟回滾

到此為止,已經對事務的A(原子性)和D(持久性)有了一個全面的理解,接下來將討論I的實現。在此先對Redo Log做一個總結:
(1) 一個事務對應多條Redo Log,事務的Redo Log不是連續存儲的。
(2) Redo Log不保證事務的原子性,而是保證了持久性。無論提交的,還是未提交事務的日誌,都會進入Redo Log。從而使得Redo Log回放完畢,資料庫就恢復到宕機之前的狀態,稱為Repeating History。
(3) 同時,把未提交的事務挑出來並回滾。回滾通過Checkpoint記錄的“活躍事務表”+ 每個事務日誌中的開始/結束標記 + Undo Log 來實現。
(4) Redo Log具有冪等性,通過每個Page裡面的pageLSN實現。
(5) 無論是提交的、還是未提交的事務,其對應的Page數據都可能被刷到了磁碟中。未提交的事務對應的Page數據,在宕機重啟後會回滾。
(6) 事務不存在“物理回滾”,所有的回滾操作都被轉化成了Commit。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.MongoDB數據歸檔的意義 和其他類型的資料庫一樣,歸檔對MongoDB同樣重要。通過歸檔,可以保持集合中合適的數據量,對資料庫的性能是一種保障,也就是大家常說的數據冷熱分離。 同時,歸檔對資料庫的管理也帶來了很大方便性,例如日常的備份、災難恢復等。 在此,不再展開敘述了。 2.集合數據歸檔流 ...
  • 使用Python3操作MySQL資料庫:創建表,插入數據,查詢數據,更新數據,刪除數據。 ...
  • Redis在3.0版本以後開始支持集群,經過中間幾個版本的不斷更新優化,最新的版本集群功能已經非常完善。本文簡單介紹一下Redis集群搭建的過程和配置方法,redis版本是5.0.4,操作系統是中標麒麟(和Centos內核基本一致)。 1、Redis集群原理 Redis 集群是一個提供在多個Redi ...
  • 首先資料庫的外鍵是資料庫提供的一種完整性約束。在許多資料庫的書上也會介紹到,然而對於外鍵這個完整性性約束究竟應該在資料庫端實現,還是在項目業務端實現很多人有著不同的意見。 在查看了很多大牛的文章後我總結出來如下: 個人開發(小型應用)、資料庫讀寫資源充足(資料庫併發低),集中式資料庫系統,則應該使用 ...
  • 在Oracle資料庫中,兩個表之間的表連接方法有排序合併連接、嵌套迴圈連接、哈希連接和笛卡爾連接四種 1.排序合併連接(sort merge join) 排序合併連接是一種兩表在做表連接時用排序(SORT)操作和合併(MERGE)操作來得到連接結果集的表連接方法 如果t1表和t2表在做表連接時使用的 ...
  • 1.線上釋放記憶體 use admindb.runCommand({closeAllDatabases:1}) 註:3.2 版本 已經去掉了這個命令了 2.rs.status() 查詢複製集狀態 3.db.stats() 查詢指定庫的狀態(包含記憶體和磁碟的使用情況統計) 4.db.collection ...
  • 類型:View Owner: SYS 內容: 記錄了該用戶可訪問的所有資料庫對象的腳本信息(DDL) 欄位: OWNER: 對象的Owner NAME: 對象名稱 TYPE: 對象類型,如FUNCTION, JAVA SOURCE, PACKAGE, PACKAGE BODY, PROCEDURE, ...
  • 6.5 事務實現原理之1:Redo Log 介紹事務怎麼用後,下麵探討事務的實現原理。事務有ACID四個核心屬性:A:原子性。事務要麼不執行,要麼完全執行。如果執行到一半,宕機重啟,已執行的一半要回滾回去。C:一致性。各種約束條件,比如主鍵不能為空、參照完整性等。I:隔離性。隔離性和併發性密切相關, ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...