天天看點

MySQL8.0新特性:增加系統檔案追蹤space ID和實體檔案的映射

update:從8.0.11開始,又改成了打開全部ibd檔案,但是改成了并行掃描

Note1: 本文所有代碼相關的内容都是基于MySQL8.0.3,而目前版本還處于RC和快速開發的狀态,不排除後面的版本邏輯,函數名等發生變化。

Note3: 本文僅是本人的閱碼筆記,記錄的比較淩亂。。。

實作的思路其實不複雜,就是将所有的表空間ID和對應的路徑資訊存儲到系統檔案中,在崩潰恢複時再按需打開。

那麼如何保證所有的表空間資訊都一個不漏的存儲到系統檔案了呢 ? 實際上他跟蹤了所有的表空間檔案操作,并更新記憶體cache中(<code>Fil_Open::m_spaces</code>), 如下:

打開表空間檔案後,寫一條日志MLOG_FILE_OPEN, 并将表空間狀态 Nodes::OPEN以及日志end lsn在記憶體中進行更新(Fil_Open::Nodes::load)

關閉表空間檔案後, 将緩存的表空間資訊LSN重置為0,并将狀态設定為CLOSED (<code>Fil_Open::Nodes::close</code>)

在實體rename檔案之前, 将新的表空間名通過MLOG_FILE_OPEN寫到redo log中,記錄新檔案的狀态到記憶體。

随後就将緩存的表空間資訊寫到系統映射檔案中(<code>Fil_Open::to_file</code>)

在實體删除檔案之後,将對應的表空間狀态設定為DELETED (<code>Fil_Open::deleted</code>)

在實體建立表空間檔案之後, 調用<code>Fil_Open::open</code> 将新檔案的資訊存儲到記憶體中。同樣的包含建立檔案時的LSN

可見InnoDB在對檔案進行打開,關閉,建立,删除,重命名這些操作時都進行了追蹤,其中CREATE/DELETE/RENAME的cache更新均發生在記錄對應的MLOG_FILE_*日志之前。

另外我們也可以看到,表空間資訊不是直接寫入的,而是經過zip壓縮後再寫的,以減少磁盤空間占用。

那麼何時将緩存的資訊刷到磁盤呢 ?

第一種情況是rename tablespace時,會做一次寫檔案

第二種情況是做checkpoint之前會去做一次flush(<code>fil_tablespace_open_sync_to_disk</code>), 相比第一種情況,這裡先做一次清理(<code>Fil_Open::purge -&gt; Fil_Open::Nodes::purge</code>),将狀态為DELETED/MISSING的無效表空間記錄删除掉,再刷到磁盤

當系統正常關閉時,InnoDB會去将系統檔案中的資訊全部清除掉(<code>fil_tablespace_open_clear</code>),因為崩潰恢複無需用到。

那麼崩潰恢複時,如何使用該檔案呢?

首先在啟動時(<code>srv_start</code>), 當确定了需要崩潰恢複時(<code>recv_recovery_from_checkpoint_start</code>),就會去從系統映射檔案中載入表空間資訊到記憶體中(<code>fil_tablespace_open_init_for_recovery --&gt; Fil_Open::from_file</code>)。

随後開始讀redo log并解析, 如下堆棧:

在将redo log加入到hash table之前,會先進行判斷,隻有在檔案中找到的表空間,才需要去apply日志。

由于系統檔案不是實時flush的,是以在解析到MLOG_FILE_*類型的redo時, 也要對緩存的表空間資訊進行修正(<code>fil_tablespace_name_recover --&gt; fil_name_process_for_recovery</code>) ,以確定所有需要apply redo的tablespace都load到記憶體中。

在執行崩潰恢複時,InnoDB會按需去打開表空間檔案,然後再去apply日志。(<code>recv_apply_hashed_log_recs --&gt; fil_tablespace_open_for_recovery</code>),隻有那些需要做崩潰恢複的檔案,才會被打開。