7. 事务隔离的具体实现
不可重复读:
事务开启的时候创建视图 -> 之后事务执行期其他事务修改了数据,当前事务查看到的和开始事务时的一样。从此看出,事务读数据的时候是隔离的。
但是当前事务修改数据的时候,如果其他事务先对该行数据进行修改,则触发行锁,需要等其他事务执行完,释放锁后,才能执行。这里感觉事务又不是隔离的。
从下面的例子中来解释
mysql> CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);
注意:
- start/begin transaction : 用此命令开启的事务,视图不是马上创建,而是在执行到它们之后的第一个操作InnoDB语句,事务才真正的启动
- start transaction witch consistent snapshot :马上启动事务,创建视图
- 这里默认是autocommit =1
查询结果:
- 事务a:查询的数据是1
- 事务b:查询的数据是3
- 事务c: 没有显示开启/提交事务,但是update语句本身就一个事务,语句完成后就自动提交
解释:
mysql中有两个视图概念:
- view:查询语句定义的虚拟表,通过执行查询语句生成的结果创建视图。创建语法 create view,查询方法和表一样。
- 一致性读视图(用于InnoDB实现MVCC):用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。
这里讲解的是“一致性读视图”,下面简称视图。
视图生成方式:
InnoDB 利用了“所有数据都有多个版本”的这个特性,实现了“秒级创建快照”的能力。
- 事务启动时,生成基于整个数据库视图。
- 生成方式:不是将现有的数据都copy一份
- 事务启动,去InnoDB申请一个顺序唯一的事务ID(tra_id)
- 每行数据也与一个row tra_id
- 事务更新数据的时候,会将tra_id 赋值给修改行的row tra_id,同时该行的旧数据和row tra_id也会保留(不是物理存储,而是通过undo log和当前版本推算出的)
- 行数据修改的过程,会生成 undo log(回滚日志),下图中的 u1、u2、u3
- v1、v2、v3不是物理存在的,而是通过当前版本和undo log计算出来的
视图作用流程:
- 事务开始,记录当前所有“活跃事务-没有提交的事务”记成一个数组
- 低水位:事务id最小值
- 高水位:事务id最大值+1(就是当前事务id)
- 查询数据的时候,通过row tra_id 和 事务数组进行对比来判断,该数据时候可显示
- 在事务数组中:
- 是当前事务id:当前事务进行修改的,可显示
- 当前事务开启的事务还没提交:不能显示
- 不在事务数组中:
- 比低水位小:已经提交的事务,可以显示
- 比高水位高:是当前事务开启后,开启的事务修改的,不能显示
举例:
流程: