閱讀813 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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

  上一篇:go  信息被占用問題
  下一篇:go  曙光“數據中國”讓全社會共享數據價值