作者:高鵬(重慶八怪)
轉載至:知數堂
原文地址: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)
調用 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;
}
}