InnoDB undo log物理结构的初始化

导 读

作者:高鹏(重庆八怪)

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

作者按:一直以来,未好好学习Innodb 的undo,最近刚好有点时间准备学习一下,本文通过阿里内核月报和自己看代码进行综合总结,如果有误,欢迎拍砖~

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

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

  • 代码版本 percona 5.7.22

  • 参数 innodb_undo_tablespaces = 4 及使用了4个undo tablespace

  • 参数 innodb_rollback_segments = 128

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

一、undo 表空间物理文件的建立

本过程调用函数srv_undo_tablespaces_init进行,栈帧如下:

1#0  srv_undo_tablespaces_init (create_new_db=true, n_conf_tablespaces=4, n_opened=0x2ef55b0) 2    at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/srv/srv0start.cc:824 3#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 4#2  0x00000000019ca74e in innobase_init (p=0x2f2a420) at /root/mysqlc/percona-server-locks-detail-5.7.22/storage/innobase/handler/ha_innodb.cc:4409 5#3  0x0000000000f7ec2a in ha_initialize_handlerton (plugin=0x2fca110) at /root/mysqlc/percona-server-locks-detail-5.7.22/sql/handler.cc:871 6#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的配置的个数

    /** Default undo tablespace size in UNIV_PAGEs count (10MB). */ 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文件 ...

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

1/* Create the undo spaces only if we are creating a new 2    instance. We don't allow creating of new undo tablespaces 3    in an existing instance (yet).  This restriction exists because 4    we check in several places for SYSTEM tablespaces to be less than 5    the min of user defined tablespace ids. Once we implement saving 6    the location of the undo tablespaces and their space ids this 7    restriction will/should be lifted. */

简单的讲就是建立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部分代码如下:

1mlog_write_ulint(header + FSP_SPACE_ID, space_id, MLOG_4BYTES, mtr); 2    mlog_write_ulint(header + FSP_NOT_USED, 0, MLOG_4BYTES, mtr); 3 4    mlog_write_ulint(header + FSP_SIZE, size, MLOG_4BYTES, mtr); 5    mlog_write_ulint(header + FSP_FREE_LIMIT, 0, MLOG_4BYTES, mtr); 6    mlog_write_ulint(header + FSP_SPACE_FLAGS, space->flags, 7             MLOG_4BYTES, mtr); 8    mlog_write_ulint(header + FSP_FRAG_N_USED, 0, MLOG_4BYTES, mtr); 9 10    flst_init(header + FSP_FREE, mtr); 11    flst_init(header + FSP_FREE_FRAG, mtr); 12    flst_init(header + FSP_FULL_FRAG, mtr); 13    flst_init(header + FSP_SEG_INODES_FULL, mtr); 14    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)信息如下:

1/* Create the trx sys file block in a new allocated file segment */ 2    block = fseg_create(TRX_SYS_SPACE, 0, TRX_SYS + TRX_SYS_FSEG_HEADER, 3                mtr); //建立segment 4    buf_block_dbg_add_level(block, SYNC_TRX_SYS_HEADER); 5 6    ut_a(block->page.id.page_no() == TRX_SYS_PAGE_NO); 7 8    page = buf_block_get_frame(block); //获取内存位置 9 10    mlog_write_ulint(page + FIL_PAGE_TYPE, FIL_PAGE_TYPE_TRX_SYS, //写入block 的类型 11             MLOG_2BYTES, mtr); 12 13    ... 14    /* Start counting transaction ids from number 1 up */ 15    mach_write_to_8(sys_header + TRX_SYS_TRX_ID_STORE, 1); // 初始化TRX_SYS_TRX_ID_STORE 16 17    /* Reset the rollback segment slots.  Old versions of InnoDB 18    define TRX_SYS_N_RSEGS as 256 (TRX_SYS_OLD_N_RSEGS) and expect 19    that the whole array is initialized. */ 20    ptr = TRX_SYS_RSEGS + sys_header; 21    len = ut_max(TRX_SYS_OLD_N_RSEGS, TRX_SYS_N_RSEGS) 22        * TRX_SYS_RSEG_SLOT_SIZE;//TRX_SYS_OLD_N_RSEGS 为256个 23    memset(ptr, 0xff, len); //将slot的信息的全部初始化为ff 24    ptr += len; 25    ut_a(ptr <= page + (UNIV_PAGE_SIZE - FIL_PAGE_DATA_END)); 26 27    /* Initialize all of the page.  This part used to be uninitialized. */ 28    memset(ptr, 0, UNIV_PAGE_SIZE - FIL_PAGE_DATA_END + page - ptr); //将剩下的空间设置为0x00 29 30    mlog_log_string(sys_header, UNIV_PAGE_SIZE - FIL_PAGE_DATA_END 31            + page - sys_header, mtr); 32 33    /* Create the first rollback segment in the SYSTEM tablespace */ 34    slot_no = trx_sysf_rseg_find_free(mtr, false, 0); 35    page_no = trx_rseg_header_create(TRX_SYS_SPACE, univ_page_size, 36                     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的定位:

    /** Transaction system header / /------------------------------------------------------------- @{ / #define TRX_SYS_TRX_ID_STORE    0   /!< the maximum trx id or trx                    number modulo                    TRX_SYS_TRX_ID_UPDATE_MARGIN                    written to a file page by any                    transaction; the assignment of                    transaction ids continues from                    this number rounded up by                    TRX_SYS_TRX_ID_UPDATE_MARGIN                    plus                    TRX_SYS_TRX_ID_UPDATE_MARGIN                    when the database is                    started /  //最大的事物ID,下次实例启动会加上TRX_SYS_TRX_ID_UPDATE_MARGIN启动 #define TRX_SYS_FSEG_HEADER 8   /!< segment header for the                    tablespace segment the trx                    system is created into / #define TRX_SYS_RSEGS       (8 + FSEG_HEADER_SIZE)                    /!< the start of the array of                    rollback segment specification                    slots ///指向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

1static MYSQL_SYSVAR_ULONG(rollback_segments, srv_rollback_segments, 2  PLUGIN_VAR_OPCMDARG, 3  "Number of rollback segments to use for storing undo logs.", 4  NULL, NULL, 5  TRX_SYS_N_RSEGS,  /* Default setting */ 6  1,            /* Minimum value */ 7  TRX_SYS_N_RSEGS, 0);  /* Maximum value */

参数 innodb_undo_logs

1static MYSQL_SYSVAR_ULONG(undo_logs, srv_undo_logs, 2  PLUGIN_VAR_OPCMDARG, 3  "Number of rollback segments to use for storing undo logs. (deprecated)", 4  NULL, innodb_undo_logs_update, 5  TRX_SYS_N_RSEGS,  /* Default setting */ 6  1,            /* Minimum value */ 7  TRX_SYS_N_RSEGS, 0);  /* Maximum value */

TRX_SYS_N_RSEGS 就是128

下面是注释和代码

1 /* Deprecate innodb_undo_logs.  But still use it if it is set to 2    non-default and innodb_rollback_segments is default. */ 3 4    if (srv_undo_logs < TRX_SYS_N_RSEGS) { 5        ib::warn() << deprecated_undo_logs; 6        if (srv_rollback_segments == TRX_SYS_N_RSEGS) { 7            srv_rollback_segments = srv_undo_logs; 8        } 9    }
  • 初始化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信息

1/* Initialize max size field */ 2    mlog_write_ulint(rsegf + TRX_RSEG_MAX_SIZE, max_size, 3             MLOG_4BYTES, mtr); 4 5    /* Initialize the history list */ 6 7    mlog_write_ulint(rsegf + TRX_RSEG_HISTORY_SIZE, 0, MLOG_4BYTES, mtr); 8    flst_init(rsegf + TRX_RSEG_HISTORY, mtr);

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

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

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

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

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

    /* Transaction rollback segment header / /-------------------------------------------------------------/ #define TRX_RSEG_MAX_SIZE   0   / Maximum allowed size for rollback                    segment in pages / #define TRX_RSEG_HISTORY_SIZE   4   / Number of file pages occupied                    by the logs in the history list / //history 链表大小 #define TRX_RSEG_HISTORY    8   / The update undo logs for committed                    transactions / //链表头base node  他们通常调用include/fut0lst.ic中的函数进行更改 #define TRX_RSEG_FSEG_HEADER    (8 + FLST_BASE_NODE_SIZE)                    / Header for the file segment where                    this page is placed / #define TRX_RSEG_UNDO_SLOTS (8 + FLST_BASE_NODE_SIZE + FSEG_HEADER_SIZE)                    / Undo log segment slots / // /-------------------------------------------------------------*/

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

1/* We define the field offsets of a base node for the list */ 2#define FLST_LEN    0   /* 32-bit list length field */ 3#define FLST_FIRST  4   /* 6-byte address of the first element 4                of the list; undefined if empty list */ 5#define FLST_LAST   (4 + FIL_ADDR_SIZE) /* 6-byte address of the 6                last element of the list; undefined 7                if empty list */ 8 9#define FIL_ADDR_PAGE   0   /* first in address is the page offset */ 10#define FIL_ADDR_BYTE   4   /* then comes 2-byte byte offset within page*/ 11#endif /* !UNIV_INNOCHECKSUM */ 12#define FIL_ADDR_SIZE   6   /* address size is 6 bytes */

多了一个长度

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

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

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

undo phy5.jpg

最终,我们看到初始化完成后undo segment slot指向的都是FIL_NULL,及没有指向,当实际分配的时候这些slot就会指向我们的undo segment header。

同时我们可以看看undotablespace到底包含哪些类型块,使用自制的小工具读取如下:

1./myblock undo001 -d|more 2 3current read blocks is : 0 --This Block is file space header blocks! 4current read blocks is : 1 --This Block is insert buffer bitmap  blocks! 5current read blocks is : 2 --This Block is inode blocks! 6current read blocks is : 3 --This Block is system   blocks! 7current read blocks is : 4 --This Block is system   blocks! 8current read blocks is : 5 --This Block is system   blocks! 9current read blocks is : 6 --This Block is system   blocks! 10current read blocks is : 7 --This Block is system   blocks! 11current read blocks is : 8 --This Block is system   blocks! 12current read blocks is : 9 --This Block is system   blocks! 13current read blocks is : 10 --This Block is system   blocks! 14current read blocks is : 11 --This Block is system   blocks! 15current read blocks is : 12 --This Block is system   blocks! 16current read blocks is : 13 --This Block is system   blocks! 17current read blocks is : 14 --This Block is system   blocks! 18current read blocks is : 15 --This Block is system   blocks! 19current read blocks is : 16 --This Block is system   blocks! 20current read blocks is : 17 --This Block is system   blocks! 21current read blocks is : 18 --This Block is system   blocks! 22current read blocks is : 19 --This Block is system   blocks! 23current read blocks is : 20 --This Block is system   blocks! 24current read blocks is : 21 --This Block is system   blocks! 25current read blocks is : 22 --This Block is system   blocks! 26current read blocks is : 23 --This Block is system   blocks! 27current read blocks is : 24 --This Block is system   blocks! 28current read blocks is : 25 --This Block is system   blocks! 29current read blocks is : 26 --This Block is system   blocks! 30current read blocks is : 27 --This Block is undo blocks! 31current read blocks is : 28 --This Block is undo blocks! 32current read blocks is : 29 --This Block is undo blocks! 33current read blocks is : 30 --This Block is undo blocks! 34current read blocks is : 31 --This Block is undo blocks! 35current read blocks is : 32 --This Block is undo blocks! 36current read blocks is : 33 --This Block is undo blocks! 37current read blocks is : 34 --This Block is undo blocks! 38current read blocks is : 35 --This Block is undo blocks! 39current read blocks is : 36 --This Block is undo blocks! 40current read blocks is : 37 --This Block is undo blocks! 41current read blocks is : 38 --This Block is new allocate blocks! 42current read blocks is : 39 --This Block is new allocate blocks! 43current read blocks is : 40 --This Block is new allocate blocks! 44current read blocks is : 41 --This Block is new allocate blocks! 45current read blocks is : 42 --This Block is new allocate blocks!

这里 block3-block26 就是我们的rollback segment header block。我这里当然是 4个undo tablespace的情况,看的是undo tablespace 1。看来没有问题。分析正确。

五、总结

  • 普通的undo segment的关联方式是:ibdata的block 5 system segment header通过33-128这些 rollback segment slot 轮询指向不同的undo tablespace 的rollback segment header,然后每个rollback segment header中有1024个slot来指向实际的undo segment header,来实现的。实际的undo block会挂载到undo segment header下的链表中。

  • undo tablespaces数量的变化只能通过重新初始化实例来改变,space id是固定了,所以要考虑清楚

  • innodb_undo_tablespaces是undo tablespace的数量而innodb_rollback_segments是 rollback segment的数量,参数innodb_undo_logs已经过时了,它和innodb_rollback_segments是同样的功能,默认他们都是128

  • rollback segment slot 0 固定在 ibdata中,而 rollback segment slot 1-32 为临时rollback segment,33-128才是普通事物的rollback segment。

参考文献:

阿里内核月报:http://mysql.taobao.org/monthly/2015/04/01/

对本文有任何疑问可扫码添加原文作者微信

加入知数堂

挑战40万+年薪!

知数堂

叶金荣与吴炳锡联合打造

领跑IT精英培训

行业资深专家强强联合,倾心定制

MySQL实战/MySQL优化/MongoDB/

Python/ SQL优化/Hadoop+ELK

数门精品课程

“阅读原文”可获更多正课试听视频

密码:hg3h

紧随技术发展趋势,定期优化培训教案

融入大量生产案例,贴合企业一线需求

社群陪伴学习,一次报名,可学1年

DBA、开发工程师必修课

上千位学员已华丽转身,薪资翻番,职位提升

改变已悄然发生,你还在等什么?

扫码加入QQ技术交流群

MySQL 8.0|MGR研究院-ZST

**(QQ群号:**650149401)

本文分享自微信公众号 - 老叶茶馆(iMySQL_WX)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )