MySQL系列:innodb源碼分析之redo log恢復

在上一篇《innodb源碼分析之重做日誌結構》中我們知道redo log的基本結構和日誌寫入步驟,那麼redo log是怎麼進行數據恢復的呢?在什麼時候進行redo log的日誌推演呢?redo log的推演只有在數據庫異常或者關閉後,數據庫重新啓動時會進行日誌推演,將數據庫狀態恢復到關閉前的狀態。那麼這個過程是怎麼進行的呢?以下我們逐步來解析。

1.recv_sys_t結構

 innodb在MySQL啓動的時候,會對重做日誌文件進行日誌重做,重做日誌是通過一個recv_sys_t的結構來進行數據恢
復和控制的。它的結構如下:
struct recv_sys_struct
{
 mutex_t	 mutex;                                 /*保護鎖*/
 ibool	 apply_log_recs;                        /*正在應用log record到page中*/
 ibool	 apply_batch_on;                     /*批量應用log record標誌*/
 
 dulint	 lsn;
 ulint	 last_log_buf_size;

 byte*	 last_block;                             /*恢復時最後的塊內存緩衝區*/
 byte*	 last_block_buf_start;             /*最後塊內存緩衝區的起始位置,因爲last_block是512地址對齊的,需要這個變量記錄free的地址位置*/
 byte*	 buf;                                        /*從日誌塊中讀取的重做日誌信息數據*/
 ulint	 len;	 /*buf有效的日誌數據長度*/

 dulint	 parse_start_lsn;                       /*開始parse的lsn*/
 dulint	 scanned_lsn;                           /*已經掃描過的lsn序號*/

 ulint	 scanned_checkpoint_no;          /*恢復日誌的checkpoint 序號*/
 ulint	 recovered_offset;                       /*恢復位置的偏移量*/

 dulint	 recovered_lsn;                         /*恢復的lsn位置*/
 dulint	 limit_lsn;                                  /*日誌恢復最大的lsn,暫時在日誌重做的過程沒有使用*/

 ibool	 found_corrupt_log;                   /*是否開啓日誌恢復診斷*/

 log_group_t*	archive_group;

 mem_heap_t*	 heap;                             /*recv sys的內存分配堆,用來管理恢復過程的內存佔用*/
 hash_table_t*	addr_hash;                     /*recv_addr的hash表,以space id和page no爲KEY*/
 ulint	 n_addrs;                                        /*addr_hash中包含recv_addr的個數*/
};
在這個結構中,比較複雜的是addr_hash這個哈希表,這個哈希表是用sapce_id和page_no作爲hash key,裏面存儲有恢復時對應的記錄內容。恢復日誌在從日誌文件中讀出後,進行解析成若干個recv_t並存儲在哈希表當中。在一個讀取解析週期過後,日誌恢復會對hash表中的recv_t中的數據寫入到ibuf和page中。這裏爲什麼要使用hash表呢?個人覺得是爲了同一個page的數據批量進行恢復的緣故,這樣可以page減少隨機插入和修改。 以下是和這個過程相關的幾個數據結構:
/*對應頁的數據恢復操作集合*/ 
struct recv_addr_struct
{
 ulint	 state;          /*狀態,RECV_NOT_PROCESSED、RECV_BEING_PROCESSED、RECV_PROCESSED*/
 ulint	 space;         /*space的ID*/
 ulint	 page_no;    /*頁序號*/
 UT_LIST_BASE_NODE_T(recv_t) rec_list;
 hash_node_t	 addr_hash;
};
/*當前的記錄操作*/
struct recv_struct
{
 byte	 type;             /*log類型*/
 ulint	 len;               /*當前記錄數據長度*/
 recv_data_t*	data;	 /*當前的記錄數據list*/
 dulint	 start_lsn;     /*mtr起始lsn*/
 dulint	 end_lsn;      /*mtr結尾lns*/
 UT_LIST_NODE_T(recv_t)	rec_list;
};
/*具體的數據體*/
struct recv_data_struct  
{
 recv_data_t*	next;	/*下一個recv_data_t,next的地址後面接了一大塊內存,用於存儲rec body*/
};
他們的內存關係結構圖如下:

2.重做日誌推演過程的LSN關係

除了這個恢復的哈希表以外,recv_sys_t中的各種LSN也是和日誌恢復有非常緊密的關係。以下是各種lsn的解釋:
    parse_start_lsn    本次日誌重做恢復起始的lsn,如果是從checkpoint處開始恢復,等於checkpoint_lsn。
    scanned_lsn        在恢復過程,將恢復日誌從log_sys->buf解析塊後存入recv_sys->buf的日誌lsn.
    recovered_lsn      已經將數據恢復到page中或者已經將日誌操作存儲addr_hash當中的日誌lsn;
    在日誌開始恢復時:
     parse_start_lsn = scanned_lsn = recovered_lsn = 檢查點的lsn。
   在日誌完成恢復時:
       parse_start_lsn =  檢查點的lsn
       scanned_lsn = recovered_lsn = log_sys->lsn。
在日誌推演過程中lsn大小關係如下:

3.日誌恢復的主要接口和流程

恢復日誌主要的接口函數:
   recv_recovery_from_checkpoint_start    從重做日誌組內的最近的checkpoint開始恢復數據
    recv_recovery_from_checkpoint_finish  結束從重做日誌組內的checkpoint的數據恢復操作
    recv_recovery_from_archive_start           從歸檔日誌文件中進行數據恢復
    recv_recovery_from_archive_finish         結束從歸檔日誌中的數據恢復操作
    recv_reset_logs                              
            截取重做日誌最後一段作爲新的重做日誌的起始位置,可能會丟失數據

重做日誌恢復數據的流程(checkpoint方式)
   1.當MySQL啓動的時候,先會從數據庫文件中讀取出上次保存最大的LSN。
    2.然後調用recv_recovery_from_checkpoint_start,並將最大的LSN作爲參數傳入函數當中。
    3.函數會先最近建立checkpoint的日誌組,並讀取出對應的checkpoint信息
    4.通過checkpoint lsn和傳入的最大LSN進行比較,如果相等,不進行日誌恢復數據,如果不相等,進行日誌恢復。
    5.在啓動恢復之前,先會同步各個日誌組的archive歸檔狀態
    6.在開始恢復時,先會從日誌文件中讀取2M的日誌數據到log_sys->buf,然後對這2M的數據進行scan,校驗其合法性,而後將去掉block header的日誌放入recv_sys->buf當中,這個過程稱爲scan,會改變scanned lsn.
    7.在對2M的日誌數據scan後,innodb會對日誌進行mtr操作解析,並執行相關的mtr函數。如果mtr合法,會將對應的記錄數據按space page_no作爲KEY存入recv_sys->addr_hash當中。
    8.當對scan的日誌數據進行mtr解析後,innodb對會調用recv_apply_hashed_log_recs對整個recv_sys->addr_hash進行掃描,並按照日誌相對應的操作進行對應page的數據恢復。這個過程會改變recovered_lsn。
    9.如果完成第8步後,會再次從日誌組文件中讀取2M數據,跳到步驟6繼續相對應的處理,直到日誌文件沒有需要恢復的日誌數據。
    10.innodb在恢復完成日誌文件中的數據後,會調用recv_recovery_from_checkpoint_finish結束日誌恢復操作,主要是釋放一些開闢的內存。並進行事務和binlog的處理。
上面過程的示意圖如下:




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