水平有限,如果有误请指出。
一直以来未对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
软考中级精品资料免费领
- 历年真题答案解析
- 备考技巧名师总结
- 高频考点精准押题
- 资料下载
- 历年真题
193.9 KB下载数265
191.63 KB下载数245
143.91 KB下载数1148
183.71 KB下载数642
644.84 KB下载数2756
相关文章
发现更多好内容- Java Swing 中常用的布局有哪些?(java swing常用布局有哪些)
- 如何通过 Java Reflection 获取泛型信息?(java reflection如何获取泛型信息)
- 如何自定义 Java 泛型通配符?(java泛型通配符怎么自定义)
- Java Spring 注解与 XML 配置的差异究竟有哪些?(java spring注解与XML配置的区别是什么)
- Java 动态线程池对性能究竟有哪些影响呢?(Java动态线程池对性能的影响)
- 在 Java 中,Guava 究竟有哪些作用呢?(java中guava的作用是什么)
- 软考高项证书能个税抵扣吗?软考高项证书个税啥时候填报?
- 在 JavaScript 中,offsetWidth 的作用究竟是什么?(javascript中offsetwidth作用是什么)
- 软考高项证书个税抵扣有用吗?软考高项证书怎么抵扣个税?
- Java 多线程究竟能否实现线程池?(Java多线程能实现线程池吗)