我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Innodb undo之 undo物理结构的初始化

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Innodb undo之 undo物理结构的初始化

水平有限,如果有误请指出。

一直以来未对Innodb 的undo进行好好的学习,最近刚好有点时间准备学习一下,通过阿里内核月报和自己看代码的综合总结一下。本文环境:

  • 代码版本 percona 5.7.22
  • 参数 innodb_undo_tablespaces = 4 及使用了4个undo tablespace
  • 参数 innodb_rollback_segments = 128

本文描述使用如上参数的设置。

一、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

本过程主要有如下几个步骤:

  • 根据参数innodb_undo_tablespaces 的配置通过调用srv_undo_tablespace_create分别进行文件建立,默认建立的大小为10M:

for (i = 0; create_new_db && i < n_conf_tablespaces; ++i) //n_conf_tablespaces 为innodb_undo_tablespaces的配置的个数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文件...

本步骤会有一个注释如下:



简单的讲就是建立undo tablespace只能在初始化实例的时候,因为space id已经固定了。

  • 分别对4个undo tablespace调用srv_undo_tablespace_open 其主要调用fil_space_create 和 fil_node_create将新建立的undo tablespace加入Innodb的文件体系。

for (i = 0; i < n_undo_tablespaces; ++i) {
....
err = srv_undo_tablespace_open(name, undo_tablespace_ids[i]); //打开UNDO文件 建立 file node...
}
  • 分别对4个undo tablespace 进行fsp header初始化

for (i = 0; i < n_undo_tablespaces; ++i) {
            fsp_header_init( //初始化fsp header 明显 space id 已经写入
                undo_tablespace_ids[i],
                SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, &mtr); //SRV_UNDO_TABLESPACE_SIZE_IN_PAGES 默认的undo大小 10MB
        }

其中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); //建立segment
    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, //写入block 的类型
             MLOG_2BYTES, mtr);
    ...    
    mach_write_to_8(sys_header + TRX_SYS_TRX_ID_STORE, 1); // 初始化TRX_SYS_TRX_ID_STORE
    
    ptr = TRX_SYS_RSEGS + sys_header;
    len = ut_max(TRX_SYS_OLD_N_RSEGS, TRX_SYS_N_RSEGS)
        * TRX_SYS_RSEG_SLOT_SIZE;//TRX_SYS_OLD_N_RSEGS 为256个
    memset(ptr, 0xff, len); //将slot的信息的全部初始化为ff
    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); //将剩下的空间设置为0x00
    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); //将第一个slot固定在ibdata中

完成了这一步过后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所在的位置。

  • 下面是system segment header的定位:

#define TRX_SYS_TRX_ID_STORE    0     //最大的事物ID,下次实例启动会加上TRX_SYS_TRX_ID_UPDATE_MARGIN启动#define TRX_SYS_FSEG_HEADER 8   #define TRX_SYS_RSEGS       (8 + FSEG_HEADER_SIZE)
                    //指向rollback segment header的槽

三、进行rollback segment header的初始化

调用 trx_sys_create_rsegs进行:

  • 说明一下关于innodb_undo_logs参数和innodb_rollback_segments参数,他们作用就是设置rollback segment 的个数,本文以128为例。

根据注释和代码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

下面是注释和代码


        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;
        }
    }
  • 初始化rollback segments 段

n_noredo_created = trx_sys_create_noredo_rsegs(n_tmp_rsegs); //创建 32个 临时rollback segments

我们这里不准备考虑临时rollback segments

  • 建立 95个(33-128) 普通rollback segments

ulint   new_rsegs = n_rsegs - n_used; //eg:128 -33 = 95
        for (i = 0; i < new_rsegs; ++i) { //对每个rollback segment进行初始化
            ulint   space_id;
            space_id = (n_spaces == 0) ? 0
                : (srv_undo_space_id_start + i % n_spaces); //获取 undo space_id 采用 取模的方式循环初始化 1 2 3 4
            ut_ad(n_spaces == 0
                  || srv_is_undo_tablespace(space_id));            if (trx_rseg_create(space_id, 0) != NULL)

我们能够注意到这里是i % n_spaces的取模方式n_spaces为我们innodb_undo_tablespaces参数设置的值,因此每个rollback segment 是轮序的方式分布到4个不同的undo tablespace中的。

  • 具体的rollback segment header初始化过程

如上是trx_rseg_create调用trx_rseg_header_create完成的。步骤大概如下:

1、建立rollback segment


block = fseg_create(space, 0, TRX_RSEG + TRX_RSEG_FSEG_HEADER, mtr); //建立一个回滚段,返回段头所在的块

2、初始化TRX_RSEG_MAX_SIZE和TRX_RSEG_HISTORY_SIZE信息


    
    mlog_write_ulint(rsegf + TRX_RSEG_MAX_SIZE, max_size,
             MLOG_4BYTES, mtr);    
    mlog_write_ulint(rsegf + TRX_RSEG_HISTORY_SIZE, 0, MLOG_4BYTES, mtr);
    flst_init(rsegf + TRX_RSEG_HISTORY, mtr);

3、初始化每个undo segment header所在的page no


    for (i = 0; i < TRX_RSEG_N_SLOTS; i++) { //TRX_RSEG_N_SLOTS 为1024 初始化每个槽 值为 4字节指向 undo segment header的page no
        trx_rsegf_set_nth_undo(rsegf, i, FIL_NULL, mtr);
    }

初始化的情况下我们看到指向的page no都是 FIL_NULL,说明没有分配任何实际的undo segment。

4、整个rollback segment 初始化完成后将space id和page no 写回到 transaction system segment header中。


sys_header = trx_sysf_get(mtr); //获取 5号 block指针 跳过  FIL_PAGE_DATA 38U trx_sysf_rseg_set_space(sys_header, rseg_slot_no, space, mtr); //设置spacetrx_sysf_rseg_set_page_no(sys_header, rseg_slot_no, page_no, mtr); //设置 no
  • 下面是 rollback segment header的结构

#define TRX_RSEG_MAX_SIZE   0   #define TRX_RSEG_HISTORY_SIZE   4    //history 链表大小#define TRX_RSEG_HISTORY    8    //链表头base node  他们通常调用include/fut0lst.ic中的函数进行更改#define TRX_RSEG_FSEG_HEADER    (8 + FLST_BASE_NODE_SIZE)
                    #define TRX_RSEG_UNDO_SLOTS (8 + FLST_BASE_NODE_SIZE + FSEG_HEADER_SIZE)
                     //

作为 base node的 TRX_RSEG_HISTORY我们可以看到定义如下


#define FLST_LEN    0   #define FLST_FIRST  4   #define FLST_LAST   (4 + FIL_ADDR_SIZE) #define FIL_ADDR_PAGE   0   #define FIL_ADDR_BYTE   4   #endif #define FIL_ADDR_SIZE   6   

多了一个长度

到这里128 rollback segment已经初始化完成,并且 每个都包含1024个 undo segment slots。

四、整个过程初始化完成后的分布图

为了让图更加美观和好理解,我这里使用的是innodb_undo_tablespaces=2的情况下作图,也就是只有2个 undo tablespace的情况。其实4个也是同样的道理,因为rollback segment slot是轮询在表空间分配的。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Innodb undo之 undo物理结构的初始化

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

目录