MySQL8.0新特性:增加係統文件追蹤space ID和物理文件的映射
Note1: 本文所有代碼相關的內容都是基於MySQL8.0.3,而目前版本還處於RC和快速開發的狀態,不排除後麵的版本邏輯,函數名等發生變化。
Note2: 主要代碼在這個commit 中,感興趣的也可以自行閱讀代碼
Note3: 本文僅是本人的閱碼筆記,記錄的比較淩亂。。。
前麵我們提到了MySQL5.7的幾個崩潰恢複產生的性能退化 為了解決崩潰恢複的效率問題, MySQL8.0對crash recovery的邏輯進行了進一步的優化。 在之前的版本中,InnoDB通過向redo log中寫入日誌來追蹤在一次checkpoint後修改過的表空間信息,這樣就無需在crash recovery時打開所有的表空間,隻需搜集哪些被影響到的表空間。而到了8.0新版本裏,采用了一種全新的方式:單獨創建了係統映射文件, 將space id及路徑信息輪換著寫到兩個指定的係統文件tablespaces.open.1 and tablespaces.open.2中(ref Fil_Open::write
)
實現的思路其實不複雜,就是將所有的表空間ID和對應的路徑信息存儲到係統文件中,在崩潰恢複時再按需打開。
係統文件更新
那麼如何保證所有的表空間信息都一個不漏的存儲到係統文件了呢 ? 實際上他跟蹤了所有的表空間文件操作,並更新內存cache中(Fil_Open::m_spaces
), 如下:
a. fil_node_open_file
fil_system->m_open.enter();
fil_system->m_open.log(node->space->id, node->name);
fil_system->m_open.exit();
打開表空間文件後,寫一條日誌MLOG_FILE_OPEN, 並將表空間狀態 Nodes::OPEN以及日誌end lsn在內存中進行更新(Fil_Open::Nodes::load)
b. fil_node_close_file
fil_system->m_open.enter();
fil_system->m_open.close(node->space->id, node->name);
fil_system->m_open.exit();
關閉表空間文件後, 將緩存的表空間信息LSN重置為0,並將狀態設置為CLOSED (Fil_Open::Nodes::close
)
c. fil_name_write_rename
fil_system->m_open.enter();
fil_system->m_open.log(space_id, new_name);
fil_system->m_open.to_file();
fil_system->m_open.exit();
在物理rename文件之前, 將新的表空間名通過MLOG_FILE_OPEN寫到redo log中,記錄新文件的狀態到內存。
隨後就將緩存的表空間信息寫到係統映射文件中(Fil_Open::to_file
)
d. fil_delete_tablespace
fil_system->m_open.enter();
fil_system->m_open.deleted(id);
fil_system->m_open.exit();
在物理刪除文件之後,將對應的表空間狀態設置為DELETED (Fil_Open::deleted
)
e. fil_ibd_create
fil_system->m_open.enter();
fil_system->m_open.open(space_id, file->name, log_get_lsn());
fil_system->m_open.exit();
在物理創建表空間文件之後, 調用Fil_Open::open
將新文件的信息存儲到內存中。同樣的包含創建文件時的LSN
可見InnoDB在對文件進行打開,關閉,創建,刪除,重命名這些操作時都進行了追蹤,其中CREATE/DELETE/RENAME的cache更新均發生在記錄對應的MLOG_FILE_*日誌之前。
另外我們也可以看到,表空間信息不是直接寫入的,而是經過zip壓縮後再寫的,以減少磁盤空間占用。
那麼何時將緩存的信息刷到磁盤呢 ?
第一種情況是rename tablespace時,會做一次寫文件
第二種情況是做checkpoint之前會去做一次flush(fil_tablespace_open_sync_to_disk
), 相比第一種情況,這裏先做一次清理(Fil_Open::purge -> Fil_Open::Nodes::purge
),將狀態為DELETED/MISSING的無效表空間記錄刪除掉,再刷到磁盤
當係統正常關閉時,InnoDB會去將係統文件中的信息全部清除掉(fil_tablespace_open_clear
),因為崩潰恢複無需用到。
崩潰恢複
那麼崩潰恢複時,如何使用該文件呢?
首先在啟動時(srv_start
), 當確定了需要崩潰恢複時(recv_recovery_from_checkpoint_start
),就會去從係統映射文件中載入表空間信息到內存中(fil_tablespace_open_init_for_recovery --> Fil_Open::from_file
)。
隨後開始讀redo log並解析, 如下堆棧:
recv_recovery_begin
|--> recv_scan_log_recs
|--> recv_parse_log_recs
|--> recv_single_rec
|--> recv_multi_rec
在將redo log加入到hash table之前,會先進行判斷,隻有在文件中找到的表空間,才需要去apply日誌。
if (space_id == TRX_SYS_SPACE
|| fil_tablespace_lookup_for_recovery(space_id)) {
recv_add_to_hash_table(
type, space_id, page_no, body,
ptr + len, old_lsn, recv_sys->recovered_lsn);
} else {
recv_sys->missing_ids.insert(space_id);
}
由於係統文件不是實時flush的,因此在解析到MLOG_FILE_*類型的redo時, 也要對緩存的表空間信息進行修正(fil_tablespace_name_recover --> fil_name_process_for_recovery
) ,以確保所有需要apply redo的tablespace都load到內存中。
在執行崩潰恢複時,InnoDB會按需去打開表空間文件,然後再去apply日誌。(recv_apply_hashed_log_recs --> fil_tablespace_open_for_recovery
),隻有那些需要做崩潰恢複的文件,才會被打開。
最後更新:2017-10-06 08:02:58