MySQL版本链 - Undo Log

1、前序准备


# 测试表
CREATE TABLE student (
	id int PRIMARY KEY,
	name VARCHAR(100)
) Engine=InnoDB CHARSET=utf8;
# 测试数据
INSERT INTO student VALUES (1, '张三');

2、Undo Log分类

2.1、insert undo log


insert undo log是在insert操作中产生的undo log。因为insert操作的记录只对事务本身可见,对于其它事务,此记录是不可见的,所以insert undo log可以在事务提交后直接删除而不需要进行purge操作。

我们以一个例子说明:

INSERT INTO student VALUES (1, '张三');

在这里插入图片描述

2.2、update undo log


update undo log是update或delete操作中产生的undo log。因为会对已经存在的记录产生影响,为了提供 MVCC机制,因此update undo log不能在事务提交时就进行删除,而是将事务提交时放到入history list上,等待purge线程进行最后的删除操作。

同样以一个例子说明:

# 1
UPDATE student SET name='李四' WHERE id=1;

MySQL版本链 - Undo Log_第1张图片

# 2
UPDATE student SET name='王五' WHERE id=1;

MySQL版本链 - Undo Log_第2张图片
为了保证事务并发操作时,在写各自的undo log时不产生冲突,InnoDB采用回滚段的方式来维护undo log的并发写入和持久化。回滚段实际上是一种Undo文件组织方式。

3、案例演示

对于使用InnoDB存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列( row_id 并不是必要的,我们创建的表中有主键或者非NULL唯一键时都不会包含 row_id 列):

  • trx_id
    每次对某条聚簇索引记录进行改动时,都会把对应的事务id赋值给trx_id隐藏列。
  • roll_pointer
    每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。

同样是student表,我们先对其进行插入操作:

# TRX 80
INSERT INTO student VALUES (2, '李四');

假设插入该记录的事务id为 80 ,那么此刻该条记录的示意图如下所示:

MySQL版本链 - Undo Log_第3张图片
假设之后两个id分别为100、200的事务对这条记录进行UPDATE操作,操作流程如下:

MySQL版本链 - Undo Log_第4张图片
注意:不能在两个事务中交叉更新同一条记录!第一个事务更新了某条记录后,就会给这条记录加锁,另一个事务再次更新时就需要等待第一个事务提交了,把锁释放之后才可以继续更新。

每次对记录进行改动,都会记录一条 undo日志,每条undo日志也都有一个roll_pointer属性( INSERT 操作对应的 undo日志 没有该属性,因为该记录并没有更早的版本),可以将这些 undo日志都连起来,串成一个链表,所以现在的情况就像下图一样:

MySQL版本链 - Undo Log_第5张图片
对该记录每次更新后,都会将旧值放到undo日志中,就算是记录的一个旧版本,随着更新次数的增多,所有的版本都会被roll_pointer属性连接成一个链表,我们把这个链表称之为版本链 ,版本链的头节点就是当前记录最新的值。另外,每个版本中还包含生成该版本时对应的事务id,这是一个非常重要的信息,它与ReadView控制隔离级别息息相关。

你可能感兴趣的:(数据库,#,MySQL,mysql)