淺析MySQL事務中的redo與undo

我們都知道事務有4種特性:原子性、一致性、隔離性和持久性,在事務中的操作,要么全部執行,要么全部不做,這就是事務的目的。事務的隔離性由鎖機制實現,原子性、一致性和持久性由事務的redo 日志和undo 日志來保證。所以本篇文章將討論關于事務中的redo和undo的幾個問題:

  • redo 日志與undo日志分別是什么?
  • redo 如何保證事務的持久性?
  • undo log 是否是redo log的逆過程?

redo log

Redo 的類型

重做日志(redo log)用來保證事務的持久性,即事務ACID中的D。實際上它可以分為以下兩種類型:

  • 物理Redo日志
  • 邏輯Redo日志

在InnoDB存儲引擎中,大部分情況下 Redo是物理日志,記錄的是數據頁的物理變化。而邏輯Redo日志,不是記錄頁面的實際修改,而是記錄修改頁面的一類操作,比如新建數據頁時,需要記錄邏輯日志。關于邏輯Redo日志涉及更加底層的內容,這里我們只需要記住絕大數情況下,Redo是物理日志即可,DML對頁的修改操作,均需要記錄Redo.

Redo 的作用

Redo log的主要作用是用于數據庫的崩潰恢復

Redo 的組成

Redo log可以簡單分為以下兩個部分:

  • 一是內存中重做日志緩沖 (redo log buffer),是易失的,在內存中
  • 二是重做日志文件 (redo log file),是持久的,保存在磁盤中

什么時候寫Redo?

上面那張圖簡單地體現了Redo的寫入流程,這里再細說下寫入Redo的時機:

  • 在數據頁修改完成之后,在臟頁刷出磁盤之前,寫入redo日志。注意的是先修改數據,后寫日志
  • redo日志比數據頁先寫回磁盤
  • 聚集索引、二級索引、undo頁面的修改,均需要記錄Redo日志。

Redo的整體流程

下面以一個更新事務為例,宏觀上把握redo log 流轉過程,如下圖所示:

mysql_redo
  • 第一步:先將原始數據從磁盤中讀入內存中來,修改數據的內存拷貝
  • 第二步:生成一條重做日志并寫入redo log buffer,記錄的是數據被修改后的值
  • 第三步:當事務commit時,將redo log buffer中的內容刷新到 redo log file,對 redo log file采用追加寫的方式
  • 第四步:定期將內存中修改的數據刷新到磁盤中

redo如何保證 事務的持久性?

InnoDB是事務的存儲引擎,其通過Force Log at Commit 機制實現事務的持久性,即當事務提交時,先將 redo log buffer 寫入到 redo log file 進行持久化,待事務的commit操作完成時才算完成。這種做法也被稱為 Write-Ahead Log(預先日志持久化),在持久化一個數據頁之前,先將內存中相應的日志頁持久化。

為了保證每次日志都寫入redo log file,在每次將redo buffer寫入redo log file之后,默認情況下,InnoDB存儲引擎都需要調用一次 fsync操作,因為重做日志打開并沒有 O_DIRECT選項,所以重做日志先寫入到文件系統緩存。為了確保重做日志寫入到磁盤,必須進行一次 fsync操作。fsync是一種系統調用操作,其fsync的效率取決于磁盤的性能,因此磁盤的性能也影響了事務提交的性能,也就是數據庫的性能。
(O_DIRECT選項是在Linux系統中的選項,使用該選項后,對文件進行直接IO操作,不經過文件系統緩存,直接寫入磁盤)

上面提到的Force Log at Commit機制就是靠InnoDB存儲引擎提供的參數 innodb_flush_log_at_trx_commit來控制的,該參數可以控制 redo log刷新到磁盤的策略,設置該參數值也可以允許用戶設置非持久性的情況發生,具體如下:

  • 當設置參數為1時,(默認為1),表示事務提交時必須調用一次 fsync 操作,最安全的配置,保障持久性
  • 當設置參數為2時,則在事務提交時只做 write 操作,只保證將redo log buffer寫到系統的頁面緩存中,不進行fsync操作,因此如果MySQL數據庫宕機時 不會丟失事務,但操作系統宕機則可能丟失事務
  • 當設置參數為0時,表示事務提交時不進行寫入redo log操作,這個操作僅在master thread 中完成,而在master thread中每1秒進行一次重做日志的fsync操作,因此實例 crash 最多丟失1秒鐘內的事務。(master thread是負責將緩沖池中的數據異步刷新到磁盤,保證數據的一致性)

fsyncwrite操作實際上是系統調用函數,在很多持久化場景都有使用到,比如 Redis 的AOF持久化中也使用到兩個函數。fsync操作 將數據提交到硬盤中,強制硬盤同步,將一直阻塞到寫入硬盤完成后返回,大量進行fsync操作就有性能瓶頸,而write操作將數據寫到系統的頁面緩存后立即返回,后面依靠系統的調度機制將緩存數據刷到磁盤中去,其順序是user buffer——> page cache——>disk。

usebuffer_pagecache_disk

除了上面談到的Force Log at Commit機制保證事務的持久性,實際上重做日志的實現還要依賴于mini-transaction。

Redo在InnoDB中是如何實現的?與mini-transaction的聯系?

Redo的實現實則跟mini-transaction緊密相關,mini-transaction是一種InnoDB內部使用的機制,通過mini-transaction來保證并發事務操作下以及數據庫異常時數據頁中數據的一致性,但它不屬于事務。

為了使得mini-transaction保證數據頁數據的一致性,mini-transaction必須遵循以下三種協議

  • The FIX Rules
  • Write-Ahead Log
  • Force-log-at-commit

The FIX Rules

修改一個數據頁時需要獲得該頁的x-latch(排他鎖),獲取一個數據頁時需要該頁的s-latch(讀鎖或者稱為共享鎖) 或者是 x-latch,持有該頁的鎖直到修改或訪問該頁的操作完成。

Write-Ahead Log

在前面闡述中就提到了Write-Ahead Log(預先寫日志)。在持久化一個數據頁之前,必須先將內存中相應的日志頁持久化。每個頁都有一個LSN(log sequence number),代表日志序列號,(LSN占用8字節,單調遞增), 當一個數據頁需要寫入到持久化設備之前,要求內存中小于該頁LSN的日志先寫入持久化設備

那為什么必須要先寫日志呢?可不可以不寫日志,直接將數據寫入磁盤?原則上是可以的,只不過會產生一些問題,數據修改會產生隨機IO,但日志是順序IO,append方式順序寫,是一種串行的方式,這樣才能充分利用磁盤的性能。

Force-log-at-commit

這一點也就是前文提到的如何保證事務的持久性的內容,這里再次總結一下,與上面的內容相呼應。在一個事務中可以修改多個頁,Write-Ahead Log 可以保證單個數據頁的一致性,但是無法保證事務的持久性,Force-log-at-commit 要求當一個事務提交時,其產生所有的mini-transaction 日志必須刷新到磁盤中,若日志刷新完成后,在緩沖池中的頁刷新到持久化存儲設備前數據庫發生了宕機,那么數據庫重啟時,可以通過日志來保證數據的完整性。

重做日志的寫入流程

重做日志寫入流程

上圖表示了重做日志的寫入流程,每個mini-transaction對應每一條DML操作,比如一條update語句,其由一個mini-transaction來保證,對數據修改后,產生redo1,首先將其寫入mini-transaction私有的Buffer中,update語句結束后,將redo1從私有Buffer拷貝到公有的Log Buffer中。當整個外部事務提交時,將redo log buffer再刷入到redo log file中。

undo log

undo log的定義

undo log主要記錄的是數據的邏輯變化,為了在發生錯誤時回滾之前的操作,需要將之前的操作都記錄下來,然后在發生錯誤時才可以回滾。

undo log的作用

undo是一種邏輯日志,有兩個作用:

  • 用于事務的回滾
  • MVCC

關于MVCC(多版本并發控制)的內容這里就不多說了,本文重點關注undo log用于事務的回滾。

undo日志,只將數據庫邏輯地恢復到原來的樣子,在回滾的時候,它實際上是做的相反的工作,比如一條INSERT ,對應一條 DELETE,對于每個UPDATE,對應一條相反的 UPDATE,將修改前的行放回去。undo日志用于事務的回滾操作進而保障了事務的原子性。

undo log的寫入時機

  • DML操作修改聚簇索引前,記錄undo日志
  • 二級索引記錄的修改,不記錄undo日志

需要注意的是,undo頁面的修改,同樣需要記錄redo日志。

undo的存儲位置

在InnoDB存儲引擎中,undo存儲在回滾段(Rollback Segment)中,每個回滾段記錄了1024個undo log segment,而在每個undo log segment段中進行undo 頁的申請,在5.6以前,Rollback Segment是在共享表空間里的,5.6.3之后,可通過 innodb_undo_tablespace設置undo存儲的位置。

undo的類型

在InnoDB存儲引擎中,undo log分為:

  • insert undo log
  • update undo log

insert undo log是指在insert 操作中產生的undo log,因為insert操作的記錄,只對事務本身可見,對其他事務不可見。故該undo log可以在事務提交后直接刪除,不需要進行purge操作。

而update undo log記錄的是對delete 和update操作產生的undo log,該undo log可能需要提供MVCC機制,因此不能再事務提交時就進行刪除。提交時放入undo log鏈表,等待purge線程進行最后的刪除。

補充:purge線程兩個主要作用是:清理undo頁和清除page里面帶有Delete_Bit標識的數據行。在InnoDB中,事務中的Delete操作實際上并不是真正的刪除掉數據行,而是一種Delete Mark操作,在記錄上標識Delete_Bit,而不刪除記錄。是一種"假刪除",只是做了個標記,真正的刪除工作需要后臺purge線程去完成。

undo log 是否是redo log的逆過程?

undo log 是否是redo log的逆過程?其實從前文就可以得出答案了,undo log是邏輯日志,對事務回滾時,只是將數據庫邏輯地恢復到原來的樣子,而redo log是物理日志,記錄的是數據頁的物理變化,顯然undo log不是redo log的逆過程。

redo & undo總結

下面是redo log + undo log的簡化過程,便于理解兩種日志的過程:

假設有A、B兩個數據,值分別為1,2.
1. 事務開始
2. 記錄A=1到undo log
3. 修改A=3
4. 記錄A=3到 redo log
5. 記錄B=2到 undo log
6. 修改B=4
7. 記錄B=4到redo log
8. 將redo log寫入磁盤
9. 事務提交

實際上,在insert/update/delete操作中,redo和undo分別記錄的內容都不一樣,量也不一樣。在InnoDB內存中,一般的順序如下:

  • 寫undo的redo
  • 寫undo
  • 修改數據頁
  • 寫Redo

小結

本文分析了事務中的redo和undo日志,參考了一些資料書籍整理得出,可能有些地方表述的不清楚。如有不對之處,歡迎指出。

參考資料 & 鳴謝

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容