天天看點

InnoDB的Redo Undo Log

原文連結:http://www.mysqlops.com/2012/04/06/innodb-log1.html

本文是介紹MySQL資料庫InnoDB存儲引擎重做日志漫遊

 – Undo Log

 Undo Log 是為了實作事務的原子性,在MySQL資料庫InnoDB存儲引擎中,還用Undo Log來實作多版本并發控制(簡稱:MVCC)。

- 事務的原子性(Atomicity)

   事務中的所有操作,要麼全部完成,要麼不做任何操作,不能隻做部分操作。如果在執行的過程中發生

   了錯誤,要復原(Rollback)到事務開始前的狀态,就像這個事務從來沒有執行過。

- 原理

   Undo Log的原理很簡單,為了滿足事務的原子性,在操作任何資料之前,首先将資料備份到一個地方

   (這個存儲資料備份的地方稱為Undo Log)。然後進行資料的修改。如果出現了錯誤或者使用者執行了

   ROLLBACK語句,系統可以利用Undo Log中的備份将資料恢複到事務開始之前的狀态。

除了可以保證事務的原子性,Undo Log也可以用來輔助完成事務的持久化。

- 事務的持久性(Durability)

   事務一旦完成,該事務對資料庫所做的所有修改都會持久的儲存到資料庫中。為了保證持久性,資料庫

   系統會将修改後的資料完全的記錄到持久的存儲上。

- 用Undo Log實作原子性和持久化的事務的簡化過程

   假設有A、B兩個資料,值分别為1,2。

   A.事務開始.

   B.記錄A=1到undo log.

   C.修改A=3.

   D.記錄B=2到undo log.

   E.修改B=4.

   F.将undo log寫到磁盤。

   G.将資料寫到磁盤。

   H.事務送出

   這裡有一個隐含的前提條件:‘資料都是先讀到記憶體中,然後修改記憶體中的資料,最後将資料寫回磁盤’。

  之是以能同時保證原子性和持久化,是因為以下特點:

   A. 更新資料前記錄Undo log。

   B. 為了保證持久性,必須将資料在事務送出前寫到磁盤。隻要事務成功送出,資料必然已經持久化。

   C. Undo log必須先于資料持久化到磁盤。如果在G,H之間系統崩潰,undo log是完整的,

      可以用來復原事務。

   D. 如果在A-F之間系統崩潰,因為資料沒有持久化到磁盤。是以磁盤上的資料還是保持在事務開始前的狀态。

缺陷:每個事務送出前将資料和Undo Log寫入磁盤,這樣會導緻大量的磁盤IO,是以性能很低。

如果能夠将資料緩存一段時間,就能減少IO提高性能。但是這樣就會喪失事務的持久性。是以引入了另外一

 種機制來實作持久化,即Redo Log.

 – Redo Log

- 原理

   和Undo Log相反,Redo Log記錄的是新資料的備份。在事務送出前,隻要将Redo Log持久化即可,

   不需要将資料持久化。當系統崩潰時,雖然資料沒有持久化,但是Redo Log已經持久化。系統可以根據

   Redo Log的内容,将所有資料恢複到最新的狀态。

- Undo + Redo事務的簡化過程

   假設有A、B兩個資料,值分别為1,2.

   A.事務開始.

   B.記錄A=1到undo log.

   C.修改A=3.

   D.記錄A=3到redo log.

   E.記錄B=2到undo log.

   F.修改B=4.

   G.記錄B=4到redo log.

   H.将redo log寫入磁盤。

   I.事務送出

- Undo + Redo事務的特點

   A. 為了保證持久性,必須在事務送出前将Redo Log持久化。

   B. 資料不需要在事務送出前寫入磁盤,而是緩存在記憶體中。

   C. Redo Log 保證事務的持久性。

   D. Undo Log 保證事務的原子性。

   E. 有一個隐含的特點,資料必須要晚于redo log寫入持久存儲。

- IO性能

   Undo + Redo的設計主要考慮的是提升IO性能。雖說通過緩存資料,減少了寫資料的IO.

   但是卻引入了新的IO,即寫Redo Log的IO。如果Redo Log的IO性能不好,就不能起到提高性能的目的。

   為了保證Redo Log能夠有比較好的IO性能,InnoDB 的 Redo Log的設計有以下幾個特點:

  A. 盡量保持Redo Log存儲在一段連續的空間上。是以在系統第一次啟動時就會将日志檔案的空間完全配置設定。

      以順序追加的方式記錄Redo Log,通過順序IO來改善性能。

   B. 批量寫入日志。日志并不是直接寫入檔案,而是先寫入redo log buffer.當需要将日志重新整理到磁盤時

      (如事務送出),将許多日志一起寫入磁盤.

   C. 并發的事務共享Redo Log的存儲空間,它們的Redo Log按語句的執行順序,依次交替的記錄在一起,

      以減少日志占用的空間。例如,Redo Log中的記錄内容可能是這樣的:

      記錄1: <trx1, insert …>

      記錄2: <trx2, update …>

      記錄3: <trx1, delete …>

      記錄4: <trx3, update …>

      記錄5: <trx2, insert …>

   D. 因為C的原因,當一個事務将Redo Log寫入磁盤時,也會将其他未送出的事務的日志寫入磁盤。

   E. Redo Log上隻進行順序追加的操作,當一個事務需要復原時,它的Redo Log記錄也不會從

      Redo Log中删除掉。

 – 恢複(Recovery)

- 恢複政策

   前面說到未送出的事務和復原了的事務也會記錄Redo Log,是以在進行恢複時,這些事務要進行特殊的

   的處理.有2中不同的恢複政策:

  A. 進行恢複時,隻重做已經送出了的事務。

   B. 進行恢複時,重做所有事務包括未送出的事務和復原了的事務。然後通過Undo Log復原那些

      未送出的事務。

- InnoDB存儲引擎的恢複機制

   MySQL資料庫InnoDB存儲引擎使用了B政策, InnoDB存儲引擎中的恢複機制有幾個特點:

  A. 在重做Redo Log時,并不關心事務性。 恢複時,沒有BEGIN,也沒有COMMIT,ROLLBACK的行為。

      也不關心每個日志是哪個事務的。盡管事務ID等事務相關的内容會記入Redo Log,這些内容隻是被當作

      要操作的資料的一部分。

   B. 使用B政策就必須要将Undo Log持久化,而且必須要在寫Redo Log之前将對應的Undo Log寫入磁盤。

      Undo和Redo Log的這種關聯,使得持久化變得複雜起來。為了降低複雜度,InnoDB将Undo Log看作

      資料,是以記錄Undo Log的操作也會記錄到redo log中。這樣undo log就可以象資料一樣緩存起來,

      而不用在redo log之前寫入磁盤了。

      包含Undo Log操作的Redo Log,看起來是這樣的:

      記錄1: <trx1, Undo log insert <undo_insert …>>

      記錄2: <trx1, insert …>

      記錄3: <trx2, Undo log insert <undo_update …>>

      記錄4: <trx2, update …>

      記錄5: <trx3, Undo log insert <undo_delete …>>

      記錄6: <trx3, delete …>

   C. 到這裡,還有一個問題沒有弄清楚。既然Redo沒有事務性,那豈不是會重新執行被復原了的事務?

      确實是這樣。同時Innodb也會将事務復原時的操作也記錄到redo log中。復原操作本質上也是

      對資料進行修改,是以復原時對資料的操作也會記錄到Redo Log中。

      一個復原了的事務的Redo Log,看起來是這樣的:

      記錄1: <trx1, Undo log insert <undo_insert …>>

      記錄2: <trx1, insert A…>

      記錄3: <trx1, Undo log insert <undo_update …>>

      記錄4: <trx1, update B…>

      記錄5: <trx1, Undo log insert <undo_delete …>>

      記錄6: <trx1, delete C…>

      記錄7: <trx1, insert C>

      記錄8: <trx1, update B to old value>

      記錄9: <trx1, delete A>

      一個被復原了的事務在恢複時的操作就是先redo再undo,是以不會破壞資料的一緻性.

- InnoDB存儲引擎中相關的函數

   Redo: recv_recovery_from_checkpoint_start()

   Undo: recv_recovery_rollback_active()

   Undo Log的Redo Log: trx_undof_page_add_undo_rec_log()