当有多个事务同时执行时,就可能出现下面几种情况:
- 脏读:对于两个事务T1与T2,T1读取了已经被T2更新但是还没有提交的字段之后,若此时T2回滚,T1读取的内容就是临时并且无效的
- 不可重复读:对于两个事务T1和T2,T1读取了一个字段,然后T2更新了该字段并提交之后,T1再次提取同一个字段,值便不相等了。
- 幻读:对于两个事务T1、T2,T1从表中读取数据,然后T2进行了INSERT操作并提交,当T1’再次读取的时候,结果不一致的情况发生。
解决这些问题的方法:
- 读未提交:表示一个事务还没有提交时,它做的变更是可以被其他事务看到。
- 读提交:表示一个事务只有在提交后,它做的表更才可以被其他事务看到。
- 可重复读:一个事务执行过程中看到的数据,总是跟这个事务启动时看到的数据是一致的。
- 串行化:写数据会加写锁,读会加读锁。当出现读写锁冲突时,后访问的事务必须等前一个事务执行完成后才可以执行。
读提交和可重复读的实现就需要借助视图(read view),视图中存储着事务需要使用数据版本,视图和数据版本的关系如下:对于一个事务视图来说,除了自己的更新总是对自己可见外,对其他的事务:
- 数据版本未提交,不可见
- 数据版本已提交,但是是在视图创建后提交的,不可见
- 数据版本已提交,而且是在视图创建前提交的,可见
可重复读会在事务开启时创建一个视图,之后读数据时会在这个视图中取数据,而在整个事务的过程中视图里面的数据版本是不会改变的,所以可重复读就可以保证在一个事务里读到的数据是一致的,这个叫做一致性读。
而读提交会在每一行Sql语句开始执行的时候创建视图,当其他事务在Sql语句执行前提交了事务版本,那该事务提交的版本对原本事务来说是可见的。
其实
begin/start transaction
命令并不是一个事务的起点,只有当执行它们后的第一个操作语句的时候,事务才会真正地启动。想要马上启动事务的语句start transaction with consistent snapshot
设置事务的隔离级别语句是:
set session transaction isolation level [隔离级别](read uncommitted|read committed|repeatable read|serializable)
默认提交语句
autocommit=1
其实在MySql里有两种视图地概念:
- 一个是“view”,普通的视图,创建视图的语法
create view...
。 - 一个是“read view”,是InnoDB在实现MVCC(多版本并发控制)时用到地一致性读视图,用于支持RC和RR的实现。
- 一个是“view”,普通的视图,创建视图的语法
快照或数据版本在MVCC中是怎么工作的?
- InnoDB里每一个事务都有一个唯一的事务ID,叫作transaction id。它是在事务开始的时候向InnoDB的事务系统申请的,并按照申请的顺序严格递增。
- 每行数据都是有多个版本的。每次事务更新数据时,都会生成一个新的数据版本,并把transaction id赋给这个数据版本的事务ID,叫做row trx_id。
- 其实V1、V2、V3这三个数据版本并不是物理上真实存在的,二十每次需要的时候根据当前版本和undo log计算出来的。图中的虚线代表了undo log(回滚日志)。
- 而在实现数据的可见性的时候,InnoDB会为每个事务构造一个数组,用来保存这个事务启动的瞬间,当前启动了但还没有提交的事务ID,数组里事务ID的最小值记为低水位,而ID的最大值记为高水位。而视图数组和高水位就组成了当前事务的一致性视图。
这样对于当前事务的启动瞬间来说,一个数据版本的 trx_id,就有以下几种可能:
如果落在绿色部分,表示这个版本是已经提交的事务或当前事务自己生成的,这个数据是可见的
如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的
如果落在黄色部分,有两种情况:
a. 若这个trx_id在这个黄色区域,表示这个版本由还没有提交地事务生成,不可见。
b. 若这个trx_id不在这个黄色区域,表示这个版本是已经提交了的事务生成的,可见。