事物的个隔离级别是非常重要的概念,Mysql的隔离级别有以下几种
在所有事物中可以看到事物没有提交的结果,实际应用中是很少的,他的性能也不比其他隔离级别好很多,读到未提交的结果导致脏读
大多数据库的默认隔离级别,但是不是mysql的默认级别,一个事物只能看到已经提交的结果,他也支持不可重复读,在同一个事物的其他实例在该实例中修改的数据,导致两次select的结果可能不一样
mysql的默认隔离级别,在事务开始的时候,直到事务结束看到的行的数据都是一样,这种隔离级别是会产生幻读,幻读就是在用户读取某一范围的数据时候,其他事物新增了一条数据,用户再次读取的时候,返现多了一行数据(幻读是指读到了其他事务的inset,不可重复读是指读到了其他事物的delete/update)
之前我们说过在可重复读级别下,事物T开始的时候会创建一个read-view,之后再事物T期间的其他事物修改了数据,对于事务T也是不可见的,但是在我上一篇说过,一个事物在修改一行数据的时候,发现这行数据已经被行锁锁住了,这个时候只能等待行锁被释放,但是在释放之后,他读取的值有事什么呢
首先,我们看一下例子如下建表语句
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);
看看上面的例子,我们看看事物A 和事物B查询到的结果是什么,此时你老想一下,(没有特别说明,默认都是autocommit=1)
事物A的结果是1,而事物B的结果是3,不知道是否和你的答案一致呢,
为什么会是这样呢,首先我们得看看数据库的一个概念视图,数据库中有定义了两种视图
快照在MVCC中是如何使用的呢
在开启一个事物的时候,就会拍个照,这个快照是对于整个库的,但是我们想象,对于一个100G的数据库,我们不可能把所有的数据全部复制一次,但是为什么开始一个事物还是很快呢,
实际上每一个事物的开启,都会创建一个事物id ,transaction id,他是向数据库申请,严格按照顺序递增的,而每一行数据都是有多个版本的,每一个事物更新数据后都会把此时的事物id,赋值给每一个版本的事物id,即row tx_id,并且保留旧的数据,并且在新的数据版本都可以直接拿到旧版本的数据,正如下图,一行数据多次更新的版本状态
如上图显示,目前最新的版本是k=22,他是的事物id=15,因此他的row trx_id=15,之前我们也提到过,语句的更新会产生undo log,其实上面的三个实线箭头就是undo log,而实际上v1,v2,v3不是真实存在的,而是有当前版本和undo log计算出来的。
在可重复读隔离级别,我们知道在事物启动的时候,只能看到事物启动前提交的数据,之后生成的版本我们是不认的,当然自己修改的数据还是要认的,
在实际应用中,每一个事物都会有一个数组,数组保存的是当前系统活跃的事物id,活跃的是指,启动但未提交的事物id。
一致性视图是有视图数组和高水位组成如下图
数据的可见性是根据数据的row trx_id和一致性视图判断的
这样,当一个事物启动的瞬间,row trx_id可能有以下几种情况
到这里,我们回过来看看开头我们的问题,为什么事务A的k=1,而事物B的k=3
此时假设一下如下
因此根据上面的可见性规则判断如下
上图中看到事物c先把(1,1)更新成了(1,2)此时row trx_id=102,然后事物B更新(1,2)为(1,3),此时的row trx_id=101,
此时我们来看看事物A查询的数据如何获取
上面的判断是从代码逻辑进行判断,其实我们可以按照下面规则进行判断
我们也验证一下上面的规则如下
但是有人会发现事物B 的update语句感觉是不有问题呢,为什么会是在(1,2)基础上进行增加的,事物C的视图不是后面才创建的吗
如果不是按照历史版本更新的话,事物c的更新不是就丢失了吗,导致读到的数据是脏读,那究竟是为什么的,这里我们要加一条规则,uodate的时候,是先读在写的,而这个读必须读取当前值,这种叫做当前读,
除了update语句外,我们的select 如果加锁,也是使用当前读,如果使用下面语句读取到的k=3
select k from t where id =1 lock in share mode
select k from t where id =1 for update
我们再进一步分析事务C如果不是自动提交,而是在下面的事务C1
此时(1,2)已经生产,但是事物没有进行提交,那么事物B的更新语句如何执行,这就要提到了上一节说的行锁,此时id=1被行锁锁住,事物B的当前读,必须等待id=1的行锁释放后,才能使用当前读。
可重复读的核心就是一致性视图,更新的时候只能用当前读,如果当前记录被行锁锁定,必须等待释放,再去更新,
读已提交和不可重复读的逻辑类似,主要有以下区别
我们再来看看读已提交的情况,如下图
事物A在获取查询语句的时候创建视图,(1,3)(1,2)此时生成的时刻是在创建视图之前,因此