1. 程式人生 > 實用技巧 >MySQL InnoDB redo Log 淺析

MySQL InnoDB redo Log 淺析

MySQL的InnoDB儲存引引擎的物理檔案儲存體系中,除了實際的資料檔案(ibd, ibdata)之外,還有兩個非常重要的日誌系統,分別是redo日誌和undo日誌。 跟Oracle類似, redo log記錄了對實際資料檔案的物理變更(資料檔案的什麼位置資料做了如何的變更)。InnoDB也是採用了WAL(日誌優先落盤),也就是說在實際資料檔案的修改落盤之前redo日誌已經落盤,從而來保證事務的永續性。Undo日誌用來保證事務的原子性和MVCC,所有的undo操作產身的資料檔案的變更也會記錄到redo日誌中。
在原生的MySQL中,redo日誌不會用來做物理主從複製,其主要的應用場景是用來進行MySQL的Crash Recovey(崩潰恢復)。關於MySQL InnoDB的崩潰恢復會在後續的文章中進行介紹。

本文主要基於MySQL 8.0介紹redo Log的基本構成。
1. redo 日誌檔案
從MySQL 5.6開始,已經廢棄了日誌組的特性(redo日誌可以寫多份),網上有觀點認為可能是InnoDB的開發團隊認為用外層的儲存硬體來保證日誌組的完整性可能更好一些。同時從5.7開始InnoDB的歸檔日誌Archive也被放棄(歸檔日誌用來歸檔存放所有的redo日誌,redo日誌系統內是採用固定尺寸的多個log檔案迴圈寫的方式來存放redo日誌,如果寫滿了會迴圈到開始的位置開始寫入)。
不過MySQL 8.0引入一個稱之為克隆的機制,從程式碼的角度來看,似乎是用來實現遠端克隆一個當前資料庫的副本,在這個機制中又引入的新的Archive歸檔機制。如果讀者有興趣可以閱讀一下MySQL8.0新版本原始碼的storage\innobase\arch和 storage\innobase\clone目錄下的程式碼。
1.1. redo Log相關引數
innodb_log_group_home_dir
該引數用來指定redo日誌存放的路徑,日誌檔案以ib_logfile[number]來命名。
innodb_log_files_in_group
雖然MySQL已經放棄了日誌組的概念,但引數名依舊保留了下來以相容以前的配置。該引數的含義為有多少個log檔案(最少為2個)。
innodb_log_file_size
表示每個檔案的大小。
所以,總的redo Log的大小為 innodb_log_files_in_group * innodb_log_file_size.

1.2. redo Log迴圈寫
redo Log以順序的方式寫入檔案,當全部檔案寫滿的時候則回到第一個檔案相應的起始位置進行覆蓋寫(但在做redo checkpoint時,也會更新第一個日誌檔案的頭部checkpoint標記,所以嚴格來講也不算順序寫),在InnoDB內部,邏輯上Redo Log被看作一個檔案,對應一個space id (InnoDB通過space的概念來組織物理儲存,包括不同的表,資料字典,redo,undo等)。

MySQL InnoDB redo Log 淺析

上圖是以指定innodb_log_files_in_group為3的迴圈寫的情況。

2. Redo Log儲存格式簡介
儘管Redo Log有多個檔案,但每個檔案的組成結構是一樣的,只是有一些資料只會存在的第一個Log檔案(ib_logfile0)的檔案頭中, 例如Buffer Pool flush checkpoint資訊只會寫在第一個log檔案的檔案頭中。
2.1. 日誌檔案儲存結構概覽
MySQL InnoDB redo Log 淺析
(ib_logfile0 儲存概覽圖)

MySQL InnoDB redo Log 淺析
(ib_logfile0 之外其它redo log檔案的儲存概覽)
InnoDB也是採用WAL的機制來保證事務的永續性,從一定的意義上來說,redo日誌是順序寫,寫入速度很快。資料庫事務導致的資料修改進入到InnoDB儲存層之後會將這些修改變更的記錄存入redo log中,然後將資料的變更寫入記憶體中的Page Pool中。InnoDB的後臺執行緒會按照一定的規則(例如定時或者髒頁的數量達到一定的比例)將髒頁落盤,落盤後會記錄下來當前redo Log中有多少變更日誌已經實際儲存到了實際的資料space檔案中。redo log的總的寫入量叫LSN(Log Secquence Numer)日誌序列號,這個redo log變更實際寫入到實際資料檔案中的數量叫checkpoint LSN,表示的是有多少變更已經實際寫入到了相應的資料檔案中。 一旦資料庫崩潰InnoDB開始恢復資料的時候,先讀取checkpoint,然後從checkpoint所指示的LSN讀取其之後的Redo日誌進行資料恢復,從而減少Crash Recovery的時間。
比較前面兩個概覽圖可以看到,checkpoint資訊只是儲存在第一個log檔案頭中。同時我們看到日誌頭中有2個checkpoint block域。InnoDB是採用2個checkpoint了輪流寫的方式來保證checkpoint的安全(並不是一次寫2份checkpoint, 而是輪流寫)。 也由於Redo log是冪等的,應用一次和與應用兩次都是一樣的(在實際的應用Redo中,如果當前這一條log的lsn大於當前page的lsn,說明這一條log還沒有被應用到當前page中去)。所以,即使某次checkpoint block寫失敗了,那麼崩潰恢復的時候從上一次記錄的checkpoint點開始恢復也能正確的恢復資料庫事務。
2.2. Log File Header
MySQL InnoDB redo Log 淺析
(Log File Header儲存結構)
Log Header Format:
這個欄位以前用來標識當前Log檔案屬於哪個日誌組,現在新的意義是用來標識當前Log為檔案的格式版本。例如如果為0則表示這個redo log是有5.7.9以前的MySQL生成的。
PAD:
沒有任何含義,目前僅僅用來做一些對齊處理。
Start LSN:
這個欄位用在Clone和Archive場景,與一般的永續性、崩潰恢復無關,這裡不做討論。
Creator:
儲存的是建立這個log檔案建立者的名稱的字串。
Left Bytes:
目前沒有任何含義,僅僅是用來填充佔位,以便讓這個block達到512位元組大小。
2.3. Log Checkpoint
MySQL InnoDB redo Log 淺析
(Checkpoint block儲存概覽)
checkpoint number:
checkpoint number可以理解為checkpoint域寫盤的次數,每次寫盤遞增1,同時這個值取模2可以實現2個checkpoint域輪流寫。
checkpoint LSN:
該欄位標示小於這個Checkpoint LSN的日誌記錄都已經寫入到了實際的資料檔案中,Crash Recovery系統從Checkpoint LSN之後的第一個MTR記錄開始進行資料恢復。
checkpoint offset:
Checkpoint LSN對應在Log files中的檔案偏移量,這個用來對LSN和Offset之間轉換進行校準。
Buf size:
MySQL系統內只對該欄位執行了寫入,併為進行讀取然後進行相應的處理。它標識的是系統當前Log buffer的大小。
Left bytes:
目前沒有任何含義,僅僅是用來填充佔位,以便讓這個block達到512位元組大小。但在這裡最後4個位元組用來存放該checkpoint域的Checksum。

2.4. Log Block的儲存格式
MySQL InnoDB redo Log 淺析
(Log Block的儲存格式)
Log Block Number:
Log Block的編號,從1開始遞增,達到最大值(0x3FFFFFFF+1)後再繼續從1開始。
Data length:
寫入到當前block的位元組數,包含頭部12位元組的大小
Firsrt Record offset
本Block內第一個mtr記錄的起始偏移量
log Block Checkpoint number
該block所處在的checkpoint no
Log Records:
一個block內可以儲存多條mtr記錄,同樣一個mtr記錄可以跨越多個block.
2.5. redo日誌邏輯格式到物理格式的對映
MySQL InnoDB redo Log 淺析
上圖是以指定innodb_log_files_in_group為2邏輯結構到物理結構的對映

上圖中上層的為Redolog的邏輯結構,可以看作是記憶體中的log buffer, 下層的為redo Log的實際物理檔案儲存,由於版面的關係,我們以innodb_log_files_in_group為2來做示例,每個log檔案中僅僅包含了2個log block(Log block的多少取決與設定的innodb_log_file_size)。
每產生一個mtr記錄就將其append到log buffer中去,當log buffer落盤的時候會獲取固定大小的資料寫入到block的資料域。當然,如果buffer中剩餘的資料不足以填滿一個block的資料域,也會將其寫入到一個新的block中,不足的資料自動不齊,block header中的data length欄位會指出有效資料的數量。

2.6. MTR簡介
MTR即Mini-transaction的縮寫,字面意思小事物,相對邏輯事物而言,我們把它稱作物理事物。屬於Innodb儲存引擎的底層模組。主要用於鎖和日誌資訊。InnoDB內部的上層模組會將事務操作轉換成若干的MTR物理事務。至於上層的事務操作如何轉換的MTR此操作,後續再另外單獨介紹,本文只介紹一下MTR記錄的格式。
每一個MTR操作會產身一條MTR Record, 下一小節我們會介紹一下MTR記錄的格式。
2.7. MTR記錄格式
用一句通俗的話來講,一條MTR記錄表示的是對哪個資料檔案(space id)的哪一頁(page)的頁內某個偏移量(offset)位置做了什麼改變(value).
MySQL InnoDB redo Log 淺析

(一條MTR記錄的通用格式)
Type:
MTR記錄的型別
Space ID:
該MTR記錄修改了哪個資料檔案
Page Number
該MTR記錄修改了哪一頁
Record Payload:
根據Type的不同,Payload內容格式也不相同,大小也不相同。後面會給出一個Type為MLOG_COMP_REC_INSERT大致的儲存結構。
(MTR Type - MLOG_COMP_REC_INSERT)
MySQL InnoDB redo Log 淺析
最後列出一些MTR Record Type, 讀者從名字應該就能看出這個Type的含義
/ one byte is written */
MLOG_1BYTE = 1,
/* 2 bytes ... /
MLOG_2BYTES = 2,
/
4 bytes ... */
MLOG_4BYTES = 4,
/ 8 bytes ... */
MLOG_8BYTES = 8,
/* Record insert /
MLOG_REC_INSERT = 9,
/
Mark clustered index record deleted */
MLOG_REC_CLUST_DELETE_MARK = 10,
/ Mark secondary index record deleted */
MLOG_REC_SEC_DELETE_MARK = 11,
/* update of a record, preserves record field sizes /
MLOG_REC_UPDATE_IN_PLACE = 13,
/!< Delete a record from a page /
MLOG_REC_DELETE = 14,
/
Delete record list end on index page */
MLOG_LIST_END_DELETE = 15,
/ Delete record list start on index page */
MLOG_LIST_START_DELETE = 16,
/* Copy record list end to a new created index page /
MLOG_LIST_END_COPY_CREATED = 17,
/
Reorganize an index page in ROW_FORMAT=REDUNDANT */
MLOG_PAGE_REORGANIZE = 18,
/ Create an index page */
MLOG_PAGE_CREATE = 19,
/* mark a compact index record as the predefined minimum record /
MLOG_COMP_REC_MIN_MARK = 36,
/
create a compact index page */
MLOG_COMP_PAGE_CREATE = 37,
/* compact record insert /
MLOG_COMP_REC_INSERT = 38,
/* mark compact clustered index record deleted /
MLOG_COMP_REC_CLUST_DELETE_MARK = 39,

本文由京東商城資料庫技術部王治提供。

轉載於:https://blog.51cto.com/wangwei007/2287431