MySQL系列:innodb源碼分析之mini transaction,mysqlinnodb
日誌是innodb一個非常重要的模組,在innodb中有兩類日誌:redo log和undo log。其中redolog日誌是用來做資料異常恢複和資料庫重啟時頁資料同步恢複的,redo log是建立在在mini transaction基礎上。資料庫在執行事務時,通過minitransaction產生redo log來保證事務的持久性。
1.mini transaction三個協議
mini-transcation是用來實現innodb的物理邏輯日誌的寫入和頁恢複的,通過mini-transcation來保證並發事務操作和資料庫異常是頁的一致性。為了得到頁的一致性,mini-transaction遵循以下三個協議:
1. The FIX Rules
2. Write-Ahead Log
3. Force-log-at-commit
1.1The FIX Rules
The FIX Rules規定如下:
修改一個頁需要獲得該頁的x-latch
訪問一個頁是需要獲得該頁的s-latch或者x-latch
持有該頁的latch直到修改或者訪問該頁的操作完成
1.2Write-Ahead Log
Write-Ahead Log的意思就是如果一個頁操作在寫入到持久裝置時,必須記憶體中相對應的日誌寫入到持久化裝置中。每個頁有一個LSN,每次頁修改需要維護這個LSN,當一個頁需要寫入到持久化裝置時,要求記憶體中小於該頁LSN的日誌先寫入到持久化裝置中。日誌寫完後,先Fixed這個頁的latch,再將記憶體中的頁刷盤。完成刷盤後,釋放頁latch。這裡遵循The FIX Rules協議。
1.3 Force-log-at-commit
一個事務可以同時修改了多個頁,Write-AheadLog單個資料頁的一致性,無法保證事務的持久性。Force -log-at-commit要求當一個事務提交時,其產生所有的mini-transaction日誌必須刷到持久裝置中。這樣即使在頁資料刷盤的時候宕機,也可以通過日誌進行redo恢複。
2 mini-transaction的日誌實現
innodb是採用mini-transaction來構建操作的物理邏輯日誌的,在事務執行的時候,會通過mtr來保證頁的資料一致性和持久性。mini-transaction是通過一個mtr_t的結構來實現mini-transaction的三個協議。mtr_t的定義如下:
typedef struct mtr_struct { ulint state; /*mtr的狀態,MTR_ACTIVE、MTR_COMMITING、MTR_COMMITTED*/ dyn_array_t memo; /*正在持有的latch列表*/ dyn_array_t log; /*mtr產生的日誌資料*/ ibool modifications; /*是否修改了頁*/ ulint n_log_recs; /*log操作頁的個數*/ ulint log_mode; /*log操作模式,MTR_LOG_ALL、MTR_LOG_NONE、MTR_LOG_SHORT_INSERTS*/ dulint start_lsn; /*mtr起始的LSN*/ dulint end_lsn; /*mtr結束的LSN*/ ulint magic_n; /*魔法字*/ }mtr_t;
其中成員memo是個latch持有狀態的數組列表,採用的是dyn_array_t的動態記憶體結構來儲存的,每個單中繼存放區的是mtr_memo_slot_t這樣的結構。定義如下:
typedef struct mtr_memo_slot_struct { ulint type; /*latch的類型值*/ void* object; /*latch物件控點,可以是rw_lock_t或者buf_block_t*/ }mtr_memo_slot_t;
latch類型如下:
MTR_MEMO_PAGE_S_FIX /*rw_locks-latch*/
MTR_MEMO_PAGE_X_FIX /*rw_lockx-latch*/
MTR_MEMO_BUF_FIX /*buf_block_t*/
MTR_MEMO_S_LOCK /*rw_lock s-latch*/
MTR_MEMO_X_LOCK /*rw_lock x-latch*/
memo的latch管理介面
mtr_memo_push 獲得一個latch,並將狀態資訊存入mtr memo當中
mtr_release_s_latch_at_savepoint 釋放memo位移savepoint的slot鎖狀態
mtr_memo_contains 判斷鎖對象是否在memo當中
mtr_memo_slot_release 釋放slot鎖的控制權
mtr_memo_pop_all 釋放所有memo中的鎖的控制權
mt_t中的log成員是也是一個dyn_array_t動態結構的記憶體,用來儲存mtr產生的日誌資訊。日誌的寫入是通過mtr0log.h來寫入的。這裡指的一提的是日誌格式,日誌格式是有日誌頭和日誌體組成,日誌頭資訊是由type、space和page no組成,由mlog_write_initial_log_record_fast函數寫入到mtr_t的log中的。以下是一個比較具體的:
log body的資料寫入是通過mtr0log.h中的日誌寫入方法進行寫入的。每寫入一跳動作記錄,n_log_recs會加1.
標識modifications是標識是否有page的資料改動,如果有,在mtr_commit調用時會先將mtr->log刷盤,然後釋放mtr所有的所控制權。日誌會一定會在mtr結束時刷盤,這符合Force-log-at-commit的規定。日誌寫入調用的是log_write_low這個函數。
2.1 mtr_t的記憶體結構關係圖
3 總結mini transaction是innodb對ACID中的持久性的最小保證單元,所有涉及到事務執行、頁資料刷盤、redo log資料恢複等都需要進行mini transaction的構造和執行。幾乎所有的模組都涉及到mini transaction,例如:btree、page、事務、inser tbuffer、redo-log等,d對mini transcaion的理解不能孤立的去看原始碼,應該結合redo log、page相關的代碼瞭解。它是理解innodb工作原理的基石。