MySQL InnoDB undo log数据结构分析

目录

一、概念解析

1、undo log基本

2、事务系统头transaction system header

3、回滚段头rollback segment header

4、回滚页头undo log page header

5、回滚日志头undo log header

6、两种undo log格式

二、数据结构

1、trx_rseg_t

2、trx_undo_t


一、概念解析

1、undo log基本

undo log是InnoDB事务中特有的结构,它的作用有两个:一是进行事务回滚(原子性),旧数据先放到undo log中,等rollback时再将旧数据里的数据回滚回来;二是MVCC(隔离性),非阻塞读。

undo能够避免脏读:在操作数据之前,将数据备份到undo页中,然后在进行数据的修改,不能看到其他回话未提交的数据,当要读取被修改页数据行时,会指向备份在undo页中的数据,避免脏读。

undo回滚段头物理存储在ibdata1文件中第6个页位置。

undo log存放在数据库内部的一个特殊段中,这个段称为undo段。undo段位于共享表空间中。回滚段实际上是一种 undo 文件组织方式

在InnoDB存储引擎中有rollback segment,每个回滚段记录了1024个undo log segment(slot),每个undo log segment段中进行undo页的申请。

从1.1版本开始InnoDB支持最大128个rollback segment,所以理论上可以支持的最大的事务上限为128*1024。

在1.2版本开始,可以通过参数对rollback segment进行设置,参数有:

参数名称

含义

innodb_undo_directory

设置回滚段文件所在的路径

innodb_undo_logs

设置回滚段的个数

innodb_undo_tablespaces

设置构成回滚段文件的数量

也就是说:

回滚段可以存放在共享表空间以外的地方,即可以设置独立表空间,innodb_undo_directory的默认值为“.”,表示当前InnoDB存储引擎的目录。

回滚段的个数默认值为128。

设置回滚段文件的数量后,回滚段可以较为平均地分布在多个文件中,在路径下可以看到undo为前缀的文件,该文件代表回滚段文件。

可用show variables like “innodb_undo%”;查询:

重新初始化时指定innodb_undo_tablespaces=N来设置,设置好后会在data目录下生成undo独立表空间(大小10M):

2、事务系统头transaction system header

回滚段的管理,在InnoDB共享表空间中,用第6个页面(5号)来管理的,这个页面专门用来存储事务系统相关信息的,源代码位于trx0sys.h,它的格式如下:

这三个参数的含义分别为:

参数名称

含义

TRX_SYS_TRX_ID_STORE

存储事务号

TRX_SYS_FSEG_HEADER

存储事务段信息

TRX_SYS_RSEGS

数组,InnoDB有128个rollback segment

3、回滚段头rollback segment header

对于每一个回滚段,即上面TRX_SYS_RSEGS数组中的一个元素,也有其自己的存储格式,代码中的宏定义位于trx0rseg.h,格式如下:

这五个信息的含义如下:

参数名称

含义

TRX_RSEG_MAX_SIZE

所有undo段页面之和

TRX_RSEG_HISTORY_SIZE

需要purge的回滚段页面数

TRX_RSEG_HISTORY

存储history list的链表首地址

TRX_RSEG_FSEG_HEADER

存储回滚段的inode位置信息

TEX_RSEG_UNDO_SLOTS

数组,1024,每个元素是一个页面号

这五个信息存储了一个回滚段的信息,最后一个位置的数组,用来真正存储回滚段的位置。这五个信息是从页面偏移38的位置开始存储,在TRX_SYS宏定义中可以查看:

4、回滚页头undo log page header

定义在trx0undo.h,事务undo日志页面头偏移量:

其中各个参数的含义为:

参数名称

含义

TRX_UNDO_PAGE_TYPE

类型,TRX_UNDO_INSERT\UPDATE

TRX_UNDO_PAGE_START

开始存储undo日志的位置

TRX_UNDO_PAGE_FREE

结束标志

TRX_UNDO_PAGE_NODE

双向链表的指针

第一个页在undo log段的回滚日志段头:

其中各个参数的含义为:

参数名称

含义

TRX_UNDO_STATE

undo段五种状态,包括TRX_UNDO_ACTIVE\CACHED\TO_FREE\TO_PURGE\PREPARED

TRX_UNDO_LAST_LOG

最后一个undo日志的偏移位置

TRX_UNDO_FSEG_HEADER

undo段inode信息

TRX_UNDO_PAGE_LIST

段链表的首地址

其中undo log段的状态有五种:

5、回滚日志头undo log header

这是回滚日志的头,在第一页的update undo log中有几个undo log headers。同样定义在trx0undo.h中,它的宏定义如下:

整理并解释其中的含义:

参数名称

含义

TRX_UNDO_TRX_ID

存储当前undo日志对应事务的事务id

TRX_UNDO_TRX_NO

事务序列号

TRX_UNDO_DEL_MARKS

删除记录标记

TRX_UNDO_LOG_START

存储当前页面中第一个undo日志的开始位置

TRX_UNDO_XID_EXISTS

标记的当前日 志中有没有包含xid事务

TRX_UNDO_DICT_TRANS

标记当前事务是不是DDL,在回滚时判断如何操作

TRX_UNDO_TABLE_ID

表id

TRX_UNDO_NEXT_LOG

链接指向下一个undo日志

TRX_UNDO_PREV_LOG

指向上一个undo日志,构成双向链表

TRX_UNDO_HISTORY_NODE

存储history list中的双向链表,链表首地址

6、两种undo log格式

undo log格式有两种:

  • insert undo log
  • update undo log

其中,insert操作会产生insert undo log。因为隔离性的要求,insert操作只对事务本身可见,对其他事务不可见。并且undo log可以在事务提交后直接删除,不需要进行purge操作。

update和delete操作都会产生update undo log。对于update操作很简单,就是将旧版本的值修改为新版本的值,并且将旧版本的记录保存到undo log中;对于delete操作完后并不是真正的删除该行数据,而是将该行的数据存在undo log中,并且将标记位delete置为true,以备回滚时可以根据标记位进行逆操作,这样就能够找回之前的旧数据,起到回滚的作用。

在delete中又分为三种情况:

purge线程两个主要作用是:清理undo页和清除page里面带有delete_bit标识的数据行。在InnoDB中,事务中的delete操作实际上并不是真正的删除掉数据行,而是在记录上标识delete_bit,而不是真正删除记录,真正的删除工作需要后台purge线程去完成。

下面是两种undo log记录的具体的结构:

二、数据结构

与undo log相关的数据结构有trx_sys_t、trx_rsegs_t 、trx_t、trx_rseg_t、trx_undo_t、trx_undo_ptr_t等结构,其中比较重要的结构有两个:trx_rseg_t和trx_undo_t。trx_rseg_t保存回滚段的信息,trx_undo_t保存关于回滚日志的信息。

通过两种方式找到回滚日志:

结论:

1、从全局变量trx_sys中的回滚段数组rseg_array中的回滚链表update-undo_list中指针(trx_sys->rseg_array[N]->update_undo_list->start)以及当前事务trx中的回滚段指针rsegs中的slot槽区m_redo中的undo log(trx->rsegs->m_redo->update_undo)都可以找到相应的回滚段中的回滚日志

2、事务id、回滚段id、page_no递增。

1、trx_rseg_t

回滚段的内存对象的类型是一个叫trx_rseg_t的结构,它定义在trx0rseg.h文件中,它的各成员有:

成员变量

解释

ulint id;

回滚段id

RsegMutex mutex;

回滚段mutex,保护

ulint space;

回滚段头空间

ulint page_no;

回滚段页码

page_size_t page_size;

页面大小

ulint max_size;

允许的最大页大小

ulint curr_size;

当前页的大小

UT_LIST_BASE_NODE_T(trx_undo_t) update_undo_list;

update回滚日志链表

UT_LIST_BASE_NODE_T(trx_undo_t) update_undo_cached;

为快速重用而缓存的update回滚日志链表

UT_LIST_BASE_NODE_T(trx_undo_t) insert_undo_list;

insert回滚日志的字段

UT_LIST_BASE_NODE_T(trx_undo_t) insert_undo_cached;

为快速重用而缓存的insert undo log链表

ulint last_page_no;

最后一个尚未清除的日志页码

ulint last_offset;

最后一个尚未清除的日志头的字节偏移量

trx_id_t last_trx_no;

最后一个尚未清除的日志的事务号

ibool last_del_marks;

判断最后一个尚未清除的日志是否要清除

ulint trx_ref_count;

跟踪rseg分配事务的参考计数器

bool skip_allocation;

如果为真,则跳过分配此rseg

update\insert_undo_list 用于保存产生的回滚日志,update\insert_undo_cached用于快速重用而缓存的回滚日志链表。当确定回滚日志无用时,会将回滚日志从update\insert_undo_list中摘除,放到相应的cached链表中。

2、trx_undo_t

回滚日志的内存对象的类型是一个叫trx_undo_t的结构,它定义在trx0undo.h文件中,它的各成员有:

成员变量

解释

ulint     id;

回滚段内的回滚日志槽号(slot)

ulint     type;

类型。TRX_UNDO_INSERT\UPDATE

ulint     state;

相应的回滚日志段的状态

ibool del_marks;

删除标记

trx_id_t     trx_id;

事务id

XID     xid;

open XA事务识别

ibool dict_operation;

是否是dict操作

table_id_t table_id;

设置表ID

trx_rseg_t* rseg;

回滚日志所属的rseg回滚段

ulint     space;

放置回滚日志的空间id

page_size_t page_size;

回滚日志中标题页的大小

ulint     hdr_page_no;

回滚日志中标题页的页码

ulint      hdr_offset;

页面上回滚日志的header偏移量

ulint     last_page_no;

登录日志中最后一页的页码

ulint     size;

当前页的大小

ulint     empty;

回滚日志记录堆栈当前是否为空

ulint     top_page_no;

链接最近撤消日志记录的页码

ulint     top_offset;

最新回滚记录的偏移量

undo_no_t top_undo_no;

最新的回滚记录的编号

buf_block_t* guess_block;

猜测可能位于首页的缓冲区

ulint     withdraw_clock;

存储guess_block时缓冲池的退出时钟值

UT_LIST_NODE_T(trx_undo_t) undo_list;

回滚段中的回滚日志对象链表

回滚段指针包括回滚段的id、日志所在的page no、以及page内偏移量。通过回滚段的指针就能够找到这个回滚段,通过回滚日志的槽号、页码、回滚记录编号和偏移量就能定位到这条回滚日志。undo_list用于将此类型的undo log链接到undo log list中。

UT_LIST_NODE_T(trx_undo_t) undo_list链表中有两个指针,分别为prev和next,用于将此undo log链接到undo log list中。

如果想对undo log的生成逻辑感兴趣,请点击MySQL InnoDB undo log生成逻辑分析

<think> 我们正在讨论undo log数据结构。根据引用[1][2][3][4],我们可以得知: - Undo logInnoDB存储引擎中用于事务回滚和MVCC的关键数据结构。 - 它属于逻辑日志,记录如何撤销事务的变更。 - 每次对数据的变更都会产生一个undo log记录,多次变更产生多条记录,且序号递增。 - undo log存储在undo log segment中,每个segment包含多个undo页(每个页16KB)。 现在,我们需要深入描述undo log数据结构。 根据MySQL官方文档和引用资料,undo log数据结构主要包含以下部分: 1. **基本结构**: - 每个undo log记录包含一些头部信息和数据部分。 - 头部信息包括:undo log类型(insert或update)、事务ID、回滚指针等。 - 数据部分:对于insert操作,记录主键信息以便删除;对于update操作,记录被修改前的数据(旧值)以及必要的信息以便重建旧版本。 2. **存储方式**: - undo log存储在回滚段(rollback segment)中,每个回滚段包含多个undo log segment,每个segment包含1024个undo slot(即可以存放1024个undo log)。 - 每个undo log segment管理一组undo页(默认为128个页,每个页16KB)。 3. **undo log记录的具体字段**(以InnoDB为例): - `TRX_ID`:产生该undo log的事务ID。 - `ROLL_PTR`:指向该记录的上一个版本的指针(用于构建版本链)。 - `UNDO_LOG_TYPE`:区分insert undo log和update undo log。 - 其他字段:根据操作类型不同而不同。 4. **两种类型的undo log**: - **Insert Undo Log**: - 由INSERT操作产生。 - 由于INSERT操作只对当前事务可见,因此该undo log在事务提交后可以立即清除。 - 结构相对简单,主要记录插入行的主键信息。 - **Update Undo Log**: - 由UPDATE或DELETE操作产生。 - 用于事务回滚和MVCC,因此需要保留到没有活动事务需要访问该版本为止。 - 结构更复杂,记录了被修改前的数据(旧值)以及索引信息等。 5. **版本链(重要)**: - 每行记录都有一个隐藏字段`DB_ROLL_PTR`(回滚指针),指向该行最近的undo log记录。 - 每个undo log记录又指向更早版本的undo log,形成一条版本链。 - 通过这个版本链,可以找到该行记录的所有历史版本,用于实现MVCC。 6. **undo页的结构**: - 每个undo页(16KB)由页头、undo log记录和页尾组成。 - 页头包含:页类型(undo页)、页状态等信息。 - undo log记录按写入顺序存储,新的记录追加在页的末尾。 7. **undo log的写入过程**(参考引用[3]): - 事务开始时,先申请一个undo log segment中的空闲位置(slot)。 - 然后申请一个undo页,将undo log记录写入该页。 - 如果当前页写满,则申请新的undo页。 8. **undo log的清理**: - 对于insert undo log,事务提交后就可以被覆盖重用。 - 对于update undo log,需要等待没有任何事务需要访问该日志时(即没有读视图会用到该版本)才能被清除。 下面我们用伪代码描述一个undo log记录的结构(以update为例): ```c struct undo_log_record { uint64 undo_no; // undo log的序号(递增) trx_id_t trx_id; // 事务ID roll_ptr_t roll_ptr; // 指向上一个版本的指针 uint8 type; // 类型,例如:UPD_EXIST_REC(更新非删除标记的记录)、DEL_REC(删除记录)等 table_id_t table_id; // 表ID // 旧版本的数据(存储被修改前的各列数据) // 对于更新操作,记录被修改的列的前镜像 // 对于删除操作,记录整行数据的前镜像 // 此外,还记录索引信息等 }; ``` 注意:实际存储中,undo log记录是可变长的,具体取决于修改的列数。 另外,引用[1]提到undo log是逻辑日志,所以它记录的是如何撤销的逻辑操作(比如反向操作),而不是物理页的修改。例如: - 对于insert操作,其undo log记录的是delete操作(通过主键删除)。 - 对于update操作,其undo log记录的是将列的值改回旧值的操作。 总结: undo log数据结构MySQL InnoDB实现事务回滚和MVCC的核心,它通过记录逻辑操作的反向操作,并组织成版本链,为数据库提供了关键的一致性保证[^1][^2][^4]。 生成相关问题: 1. undo log和redo log数据结构上有何区别? 2. InnoDB中如何管理undo log的存储空间? 3. 为什么insert undo log可以在事务提交后立即清除,而update undo log不能? 4. 如何通过undo log实现MVCC? </think> ### Undo Log 数据结构详解 Undo LogInnoDB 存储引擎中采用**链式逻辑结构**组织数据,主要包含以下核心组件: #### 1. **基础数据结构** - **`TRX_UNDO_TRX_ID`** 事务 ID(8字节),标识生成该 Undo Log 的事务[^2][^4]。 $$ \text{TRX\_UNDO\_TRX\_ID} \in \mathbb{Z}^+ $$ - **`TRX_UNDO_LOG_START`** 日志起始位置指针(16字节),指向当前 Undo Log 在页内的起始偏移量[^3]。 - **`TRX_UNDO_XID_EXISTS`** 标志位(1字节),标识事务是否包含 XA 事务信息[^2]。 - **`TRX_UNDO_DICT_TRANS`** 标志位(1字节),标识事务是否涉及数据字典操作(如 DDL)[^2]。 #### 2. **行级操作记录** 每个数据变更对应一个 **Undo Record**,结构包含: ```plaintext +-------------------+-----------------+-------------------+-------------------+ | Operation Type | Column Count | Old Values | Primary Key Info | +-------------------+-----------------+-------------------+-------------------+ ``` - **Operation Type**(1字节): 标识操作类型(INSERT/UPDATE/DELETE)。 - **Column Count**(变长): 记录涉及修改的列数量。 - **Old Values**(变长): 存储修改前的列值(逻辑格式)[^1][^4]。 - **Primary Key Info**(变长): 记录主键值,用于定位回滚目标行[^2]。 #### 3. **页内组织方式(16KB Undo Page)** ```plaintext +---------------------+---------------------+---------------------+---------------------+ | Page Header (38B) | Undo Log Header (?) | Undo Records (...) | Page Trailer (8B) | +---------------------+---------------------+---------------------+---------------------+ ``` - **Page Header**: 包含页号、LSN(日志序列号)、链表指针等元数据[^3]。 - **Undo Log Header**: 存储事务全局信息(如 TRX_ID、回滚指针)。 - **Undo Records**: 按操作顺序填充的变长记录(新记录追加在页末尾)[^3][^4]。 - **Page Trailer**: 校验和与页状态标志。 #### 4. **版本链管理** 通过 **`DB_ROLL_PTR`**(回滚指针)构建多版本链: ```mermaid graph LR A[当前行数据] --> B[Roll_Ptr=0x1234] B -->|指向| C[Undo Record#1] C -->|ROLL_PTR| D[Undo Record#2] D -->|ROLL_PTR| E[原始数据] ``` - 每次更新生成新 Undo Record,通过指针链式关联旧版本[^1][^4]。 - MVCC 读操作通过遍历该链获取历史版本[^2]。 #### 5. **类型分类结构差异** | **类型** | **INSERT Undo Log** | **UPDATE/DELETE Undo Log** | |----------------|--------------------------------|----------------------------------| | **存储内容** | 仅主键信息(用于回滚删除) | 修改前所有列值和主键[^1][^4] | | **生命周期** | 事务提交后可立即清除 | 需保留至无活动读视图依赖[^2] | | **版本链作用** | 不参与 MVCC | 构建行数据多版本链 | #### 关键特性 - **逻辑日志性质**:记录反向操作步骤(如 "SET col=old_value"),而非物理页变更[^1][^4]。 - **递增序号**:每条 Undo Record 分配唯一递增序号,确保回滚顺序正确[^4]。 - **空间复用**:已提交事务的 Undo Page 可被新事务复用(通过 `TRX_UNDO_STATE` 标记状态)[^3]。 > 示例:UPDATE 操作生成 Undo Record > ```plaintext > Type: UPDATE | ColCount: 2 | OldValues: {price=100, stock=50} | PK: id=42 > ``` > 回滚时执行:`UPDATE table SET price=100, stock=50 WHERE id=42` Undo Log 通过上述结构实现事务原子性和 MVCC,支撑 InnoDB 的高并发与数据一致性[^2][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值