【MySQL學習筆記】深入理解 redo 日誌

一、什麼是redo日誌

redo日誌,也被稱爲重做日誌,對數據庫中表的操作進行記錄,可以用於系統崩潰時的數據恢復。

例如,某個事務將系統表空間中的第100號頁面中偏移量爲1000處的那個字節的值1改爲了2,我們只需要記錄將第0號表空間的100號⻚⾯的偏移量爲1000處的值更新爲2,這樣我們在事務提交時,把上述內容刷新到磁盤中,即使之後系統崩潰了,重啓之後只要按照上述內容記錄的步驟重新更新一下數據頁,那麼該事務對數據庫中所做的修改又可以被恢復出來。

二、redo日誌的優點

與在事務提交時將所有修改過的內存中的頁面刷新到磁盤中相比,只將事務執行過程中產生的redo日誌刷新到磁盤的好處如下:

  • redo日誌佔用的空間很小
    存儲表空間ID、頁號、偏移量以及需要更新的值所需要的存儲空間是很小的。
  • redo日誌是順序寫入磁盤的
    在執行事務的過程中,每執行一條語句,就可能產生若干條redo日誌,這些日誌是按照產生的順序寫入磁盤的,也就是使用順序IO。
三、redo日誌格式

在這裏插入圖片描述各個部分詳細釋義如下:

  • type:該條redo日誌的類型
  • space ID:表空間
  • page number:頁號
  • data:該條redo日誌的具體內容
3.1 redo日誌類型
  • MLOG_1BYTE(type字段對應的⼗進制數字爲1):表示在⻚⾯的某個偏移量處寫⼊1個字節的redo⽇志類型。
  • MLOG_2BYTE(type字段對應的⼗進制數字爲2):表示在⻚⾯的某個偏移量處寫⼊2個字節的redo⽇志類型。
  • MLOG_4BYTE(type字段對應的⼗進制數字爲4):表示在⻚⾯的某個偏移量處寫⼊4個字節的redo⽇志類型。
  • MLOG_8BYTE(type字段對應的⼗進制數字爲8):表示在⻚⾯的某個偏移量處寫⼊8個字節的redo⽇志類型。
  • MLOG_WRITE_STRING(type字段對應的⼗進制數字爲30):表示在⻚⾯的某個偏移量處寫⼊⼀串數據。

在這裏插入圖片描述

四、Mini-Transaction
4.1 以組的形式寫入redo日誌

一條語句在執行過程中可能修改若干個頁面。這些頁面的更改都發生在Buffer Pool中,所以在修改完頁面之後,需要記錄下相應日誌的redo日誌。在執行語句的過程中產生的redo日誌在InnoDB中劃分成了若干個不可分割的組,比如:

  • 更新Max Row ID屬性時產生的redo日誌是不可分割的。
  • 向聚簇索引對應的B+樹的頁面中插入一條記錄時產生的redo日誌是不可分割的。
  • 向某個二級索引對應的B+樹的頁面中插入一條記錄時產生的redo日誌是不可分割的。
  • 還有其他的一些對頁面的訪問操作時產生的redo日誌也是不可分割的。

什麼是不可分割?不可分割類似於事務,即要麼都完成,要麼都不完成。

我們以向某個索引對應的B+樹插入一條記錄爲例,在向B+樹中插入這條記錄之前,需要先定位到這條記錄應該被插入到哪個葉子節點代表的數據頁中,定位到具體的數據頁之後,有兩種可能的情況:

  • 情況一:數據頁的剩餘空間充足,足夠容納這一條待插入記錄,這時,只要直接插入到這個數據頁就行,記錄一條類型爲MLOG_COMP_REC_INSER的redo日誌就好了,我們把這種情況稱之爲樂觀插入。
  • 情況二:該數據頁剩餘空間不足,這時需要進行頁分裂操作,也就是新建一個葉子節點,然後把原先數據頁中的一部分記錄複製到這個新的數據頁中,然後再把記錄插入進去,再把這個葉子節點插入到葉子節點鏈表中,最後還要在內節點中添加一條目錄項記錄指向這個新創建的頁面,很顯然,這個過程要對多個頁面進行修改,也就意味着會產生多條redo日誌,我們把這種情況稱之爲悲觀插入。

如果在悲觀插入過程中,新的頁面已經分配好了,數據也複製過去了,新的記錄也插入到該頁面中了,可是沒有向內節點中插入一條目錄項記錄,這個插入就是不完整的,這樣會形成一顆不正確的B+樹。

InnoDB中規定,在執行這些保證原子性的操作時必須以組的形式來記錄的redo日誌,要麼把全部的日誌都恢復掉,要麼一條都不恢復。如何做到這些的呢?

  • 有的需要保證原子性的操作會生成多條redo日誌,比如向某個索引對應的B+樹中進行一次悲觀插入就需要生成許多條redo日誌。如何把這些redo日誌劃分到一個組內的呢?就是在組中的最後一條redo日誌後邊加上一條特殊類型的redo日誌,該類型名稱爲MLOG_MULTI_REC_END,type字段對應的十進制數字是31,此時相當於加了一個結束標誌。這樣在系統崩潰重啓進行恢復時,只有當解析到類型爲MLOG_MULTI_REC_END的redo日誌,才認爲解析到了一組完整的redo日誌,纔會進行恢復,否則的話,直接放棄前邊解析到的redo日誌。
    在這裏插入圖片描述

  • 有時候需要保證原子性的操作只生成一條redo日誌,比如更新Max Row ID屬性的操作就只會生成一條redo日誌。其實在一條日誌後邊跟一個類型爲MLOG_MULTI_REC_END的redo日誌也是可以的,不過爲了節省空間,他們不想浪費⼀個⽐特位。 別忘了雖然redo⽇ 志的類型⽐較多, 但撐死了也就是⼏⼗種, 是⼩於127這個數字的, 也就是說我們⽤7個⽐特位就⾜以包括所有的redo⽇志類型,⽽type字段其實是佔⽤1個字節的, 也就是說我們可以省出來⼀個⽐特位⽤來表示該需要保證原⼦性的操作只產⽣單⼀的⼀
    條redo⽇志, 示意圖如下:
    在這裏插入圖片描述

4.2 Mini-Transaction的概念

顧名思義,Mini-Transaction就是微事務。把對底層頁面中的一次原子訪問的過程稱之爲一個Mini-Transaction,簡稱爲mtr,一個所謂的mtr可以包含一組redo日誌,在進行崩潰恢復時這一組redo日誌作爲一個不可分割的整體。
一個事務可以包含若干條語句,每一條語句其實是由若干個mtr組成,每一個mtr又可以包含若干條redo日誌。

五、redo日誌的寫入過程
5.1 redo日誌緩衝區

InnoDB爲了解決磁盤速度過慢的問題而引入了Buffer Pool。同理,寫入redo日誌時也不能直接寫到磁盤上,實際上在服務器啓動時就向操作系統申請了一大片稱之爲redo log buffer的連續內存空間,翻譯過來就是redo日誌緩衝區,簡稱log buffer。

5.2 redo日誌寫入log buffer

向log buffer中寫⼊redo⽇ 志的過程是順序的, 也就是先往前邊的block中寫, 當該block的空閒空間⽤完之後再往下⼀個block中寫。 當我們想往log buffer中寫⼊redo⽇ 志時, 第⼀個遇到的問題就是應該寫在哪個block的哪個偏移量處, InnoDB提供了⼀個稱之爲buf_free的全局變量, 該變量指明後續寫⼊的redo⽇ 志應該寫⼊到log buffer中的哪個位置。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章