InnoDB undo log物理結構的初始化

2021-03-02 沃趣技術

作者:高鵬(重慶八怪)

轉載至:知數堂

原文地址:http://h5ip.cn/2Iqt

一直以來未對Innodb的undo進行好好的學習,最近剛好有點時間準備學習一下,通過阿里內核月報和自己看代碼的綜合總結一下。(ps:如果有誤,歡迎拍磚~)

本文環境:

本文描述使用如上參數的設置。

| undo 表空間物理文件的建立

本過程調用函數srv_undo_tablespaces_init進行,棧幀如下:

#0  srv_undo_tablespaces_init (create_new_db=true, n_conf_tablespaces=4, n_opened=0x2ef55b0)
at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/srv/srv0start.cc:824
#1  0x0000000001bbd7e0 in innobase_start_or_create_for_mysql () at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/srv/srv0start.cc:2188
#2  0x00000000019ca74e in innobase_init (p=0x2f2a420) at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:4409
#3  0x0000000000f7ec2a in ha_initialize_handlerton (plugin=0x2fca110) at /root/mysqlc/percona-server-locks-detail-5.7.22/sql/handler.cc:871
#4  0x00000000015f9edf in plugin_initialize (plugin=0x2fca110) at /root/mysqlc/percona-server-locks-detail-5.7.22/sql/sql_plugin.cc:1252

本過程主要有如下幾個步驟:

for (i = 0; create_new_db && i < n_conf_tablespaces; ++i) 


const ulint SRV_UNDO_TABLESPACE_SIZE_IN_PAGES =
((1024 * 1024) * 10) / UNIV_PAGE_SIZE_DEF;
...
    err = srv_undo_tablespace_create(
        name, SRV_UNDO_TABLESPACE_SIZE_IN_PAGES); 
...

本步驟會有一個注釋如下:

簡單的講就是建立undo tablespace只能在初始化實例的時候,因為space id已經固定了。

for (i = 0; i < n_undo_tablespaces; ++i) {
....
err = srv_undo_tablespace_open(name, undo_tablespace_ids[i]); 
...
}

for (i = 0; i < n_undo_tablespaces; ++i) {

        fsp_header_init( 
            undo_tablespace_ids[i],
            SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, &mtr); 
    }

其中fsp_header_init部分代碼如下:

mlog_write_ulint(header + FSP_SPACE_ID, space_id, MLOG_4BYTES, mtr);
mlog_write_ulint(header + FSP_NOT_USED, 0, MLOG_4BYTES, mtr);

mlog_write_ulint(header + FSP_SIZE, size, MLOG_4BYTES, mtr);
mlog_write_ulint(header + FSP_FREE_LIMIT, 0, MLOG_4BYTES, mtr);
mlog_write_ulint(header + FSP_SPACE_FLAGS, space->flags,
         MLOG_4BYTES, mtr);
mlog_write_ulint(header + FSP_FRAG_N_USED, 0, MLOG_4BYTES, mtr);

flst_init(header + FSP_FREE, mtr);
flst_init(header + FSP_FREE_FRAG, mtr);
flst_init(header + FSP_FULL_FRAG, mtr);
flst_init(header + FSP_SEG_INODES_FULL, mtr);
flst_init(header + FSP_SEG_INODES_FREE, mtr);

這些都是fsp的內容。

做完這個步驟只是生成了4個大小為10MB的 undo tablespace文件,並且已經加入到Innodb文件體系,但是裡面沒有任何類容。

| ibdata中system segment header的初始化

本步驟調用 trx_sys_create_sys_pages->trx_sysf_create進行,本步驟除了初始化transaction system segment以外還會初始化其header( ibdata page no 5)信息如下:


block = fseg_create(TRX_SYS_SPACE, 0, TRX_SYS + TRX_SYS_FSEG_HEADER,
            mtr); 
buf_block_dbg_add_level(block, SYNC_TRX_SYS_HEADER);

ut_a(block->page.id.page_no() == TRX_SYS_PAGE_NO);

page = buf_block_get_frame(block); 

mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_TYPE_TRX_SYS, 
         MLOG_2BYTES, mtr);

...

mach_write_to_8(sys_header + TRX_SYS_TRX_ID_STORE, 1); 


ptr = TRX_SYS_RSEGS + sys_header;
len = ut_max(TRX_SYS_OLD_N_RSEGS, TRX_SYS_N_RSEGS)
    * TRX_SYS_RSEG_SLOT_SIZE;
memset(ptr, 0xff, len); 
ptr += len;
ut_a(ptr <= page + (UNIV_PAGE_SIZE - FIL_PAGE_DATA_END));


memset(ptr, 0, UNIV_PAGE_SIZE - FIL_PAGE_DATA_END + page - ptr); 

mlog_log_string(sys_header, UNIV_PAGE_SIZE - FIL_PAGE_DATA_END
        + page - sys_header, mtr);


slot_no = trx_sysf_rseg_find_free(mtr, false, 0);
page_no = trx_rseg_header_create(TRX_SYS_SPACE, univ_page_size,
                 ULINT_MAX, slot_no, mtr); 

完成了這一步過後ibdata的 block 5 就初始化完了,而且我們看到所有的rollback segment slots 都初始化完成(源碼所示有256個,實際上最多只會有128個,其中0號solt固定在ibdata中),注意這裡的槽大小是TRX_SYS_RSEG_SLOT_SIZE設置的大小為8位元組,4位元組space id ,4位元組 page no,它們會指向 rollback segment header所在的位置。



#define TRX_SYS_TRX_ID_STORE    0     
#define TRX_SYS_FSEG_HEADER 8   
#define TRX_SYS_RSEGS       (8 + FSEG_HEADER_SIZE)
                

| 進行rollback segment header的初始化

調用 trx_sys_create_rsegs進行:

根據注釋和代碼innodb_undo_logs已經是個淘汰的參數,應該用innodb_rollback_segments代替。
這兩個參數默認是就是TRX_SYS_N_RSEGS及 128 其實不用設置的。本文也用128進行討論。

參數 innodb_rollback_segments

static MYSQL_SYSVAR_ULONG(rollback_segments, srv_rollback_segments,
  PLUGIN_VAR_OPCMDARG,
  "Number of rollback segments to use for storing undo logs.",
  NULL, NULL,
  TRX_SYS_N_RSEGS,  
  1,            
  TRX_SYS_N_RSEGS, 0);  

參數 innodb_undo_logs

static MYSQL_SYSVAR_ULONG(undo_logs, srv_undo_logs,
  PLUGIN_VAR_OPCMDARG,
  "Number of rollback segments to use for storing undo logs. (deprecated)",
  NULL, innodb_undo_logs_update,
  TRX_SYS_N_RSEGS,  
  1,            
  TRX_SYS_N_RSEGS, 0);  

TRX_SYS_N_RSEGS 就是128

下面是注釋和代碼

/* Deprecate innodb_undo_logs.  But still use it if it is set to
non-default and innodb_rollback_segments is default. */

if (srv_undo_logs < TRX_SYS_N_RSEGS) {
    ib::warn() << deprecated_undo_logs;
    if (srv_rollback_segments == TRX_SYS_N_RSEGS) {
        srv_rollback_segments = srv_undo_logs;
    }
}

相關焦點

  • InnoDB undo log
    基本文件結構關鍵結構體每個回滾段對象管理undo log信息,對應的結構體為trx_undo_t
  • MySQL 日誌(redo log 和 undo log) 都是什麼鬼?
    出處:https://www.cnblogs.com/f-ck-need-u/innodb事務日誌包括redo log和undo log。redo log是重做日誌,提供前滾操作,undo log是回滾日誌,提供回滾操作。
  • MySQL InnoDB存儲引擎啟動過程源碼分析
    InnoDB相關參數的檢查和初始化,包括共享表空間、臨時表空間、undo表空間、redo日誌文件、double write文件等等。調用 innobase_start_or_create_for_mysql 函數,這個函數非常重要,後面詳細分析這個函數。
  • 必須了解的MySQL三大日誌:binlog、redo log和undo log
    作為開發,我們重點需要關注的是二進位日誌(binlog)和事務日誌(包括redo log和undo log),本文接下來會詳細介紹這三種日誌。binlogbinlog用於記錄資料庫執行的寫入性操作(不包括查詢)信息,以二進位的形式保存在磁碟中。
  • 必須了解的mysql三大日誌-binlog、redo log和undo log
    一個事務可能涉及修改多個數據頁,並且這些數據頁在物理上並不連續,使用隨機IO寫入性能太差!因此 mysql 設計了 redo log , 具體來說就是只記錄事務對數據頁做了哪些修改,這樣就能完美地解決性能問題了(相對而言文件更小並且是順序IO)。
  • MySQL底層存儲結構
    如果安裝MySQL的時候沒有配置回滾表空間,那麼查詢的結果如下:mysql> show variables like '%undo%';+-+--+| Variable_name | Value |+-+--+| innodb_max_undo_log_size | 1073741824 || innodb_undo_directory | ./
  • 詳解MySQL的Redo日誌與Undo日誌
    Innodb中各種不同的操作有著不同類型的重做日誌,類型數量有幾十種,但記錄條目的基本格式可以如下表示:圖2.1在存儲結構上,redo log文件以block塊來組織,每個block大小為512位元組。每個文件的開頭有一個2k大小的File Header區域用來保存一些控制信息,File Header之後就是連續的block。
  • 一文搞懂undo log版本鏈與ReadView機制如何讓事務讀取到該讀的數據
    為了方便理解,每一行 undo log 可以簡化為下圖所示的結構:接著事務 B(trx_id=20),將這行數據的值修改為 data_B,同樣也會記錄一條 undo log,如下圖所示,這條 undo log 的 roll_pointer 指針會指向上一個數據版本的 undo log,也就是指向事務 A 寫入的那一行 undo log。
  • 《innoDB存儲引擎》讀書筆記
    在innodb存儲引擎中,表都是根據主鍵順序組織存放的表空間由各個段組成:數據段,索引段,回滾段innodb存儲引擎是面向列的,也就是說數據是按行進行存放的b+數索引只能找到數據行所在的頁b+樹,所有的記錄都在葉子節點,並且順序存放聚集索引就是按照表的主鍵構造的一棵b+樹,同時葉子節點中存放的是整張表的行記錄數據聚集索引並不是物理上連續的,而是邏輯上連續的輔助索引並不包含行記錄的全部數據,包含鍵值和指向對應聚集索引的地址
  • MySQL如何計算統計redo log大小
    雖然我在這篇博客「MySQL中Redo Log相關的重要參數總結」中介紹了,MySQL 8.0引入了innodb_dedicated_server自適應參數,可基於伺服器的內存來動態設置innodb_buffer_pool_size,innodb_log_file_size和innodb_flush_method。默認情況下,此參數是關閉的。
  • InnoDB Page結構詳解
    innodb存儲頁類型在數據頁被初始化的時候,就會在數據頁最後(當然在checksum之前)創建兩個數據目錄,分別指向最大和最小記錄。之後插入新的數據的時候,需要維護這個目錄,例如必要的時候增加目錄的個數。每個數據目錄佔用兩個字節,存儲對應記錄的頁內偏移量。假設目錄N,這個目錄N管理目錄N-1(不包括)和目錄N之間的記錄,我們稱目錄N own 這些記錄。在目錄N指向的記錄中,會有欄位記錄own記錄的數量。
  • MySQL 引擎特性:InnoDB IO 子系統
    這裡說的相鄰有兩種含義,一種是物理上的相鄰,一種是邏輯上的相鄰。底層數據文件中相鄰,叫做物理上相鄰。如果數據文件中不相鄰,但是邏輯上相鄰(id=1的數據和id=2的數據,邏輯上相鄰,但是物理上不一定相鄰,可能存在同一個文件中不同的位置),則叫邏輯相鄰。
  • mysql分布式事務,undo和redo,java學習這塊必須要懂?
    記錄A=1到undo log.C. 修改A=3.D. 記錄B=2到undo log.E. 修改B=4.F. 將undo log寫到磁碟。G. 將數據寫到磁碟。H. 事務提交如何保證持久性?事務提交前,會把修改數據到磁碟前,也就是說只要事務提交了,數據肯定持久化了。
  • Mysql innodb 存儲引擎的性能優化
    Innodb_log_file_size2.1.1. 對於寫性能有非常重要的影響。要保持非常大。2.1.2. 高的數值會增加你回復的時間。檢查一下你能設置的最大的大小。2.1.3. 最大的限制是4G。2.2. Innodb_log_files_in_group2.2.1. 這些文件指定了對於Log所能使用的大小。
  • MySQL InnoDB 索引原理
    InnoDB表結構此小結與索引其實沒有太多的關聯,但是為了便於理解索引的內容,添加此小結作為鋪墊知識。表空間是由不同的段組成的,常見的段有:數據段,索引段,回滾段等等,在 MySQL中,數據是按照B+樹來存儲,因此數據即索引,因此數據段即為B+樹的葉子節點,索引段為B+樹的非葉子節點,回滾段用於存儲undo日誌,用於事務失敗後數據回滾以及在事務未提交之前通過undo日誌獲取之前版本的數據,在InnoDB1.1版本之前一個InnoDB