事务隔离级别
读未提交
一个事务还没提交的时候,它做的变更能够被其它事务看到
读提交
一个事务提交之后,她做的变更才会被其它事务看到
可重复读(默认)
一个事务在执行过程中看到的数据,跟这个事务在启动的时候看到的数据是一致的(一致性读)
串行化
对于同一行记录的操作,会对应家伙是那个读锁写锁,当读写冲突的时候,事务会串行执行
实际上每一种事务隔离级别都会解决一种问题,但同时也会引申出另一种问题
- 读未提交的隔离级别会产生脏读问题
- 读提交的隔离级别解决脏读问题,但是同时产生不可重复读的问题
- 可重复读的隔离级别解决了不可重复读的问题,但同时产生幻读问题
- 串行化的隔离级别解决了幻读问题
在事务隔离级别为可重复读下出现的幻读问题是指,根据某次select结构执行insert操作,在事务提交的时候,发现insert操作执行失败(引发主键冲突),就像在事务提交的一刻,表中出现了幽灵行,占用了事务想要执行insert操作的位置,所以幻读专指的是在可重复读级别下的insert操作出现幽灵行的问题,并不是在事务执行过程中,出现先后两次read的结果不一致!!
事务启动方式
begin/start transaction,配套commit
begin;
....
commit;
复制代码
begin/start命令并不是一个事务的起点,在执行begin/start之后的第一个操作InnoDB表的语句才是事务正真启动的时间点
如果想要马上启动一个事务,可以使用start transaction with consistent snapshot
命令
参数设置
set autocommit = 1;
复制代码
MVCC(多版本并发控制)
MVCC可以让InnoDB事务在某些事务隔离级别下实现一致性读的功能
一致性读
根据时间点通过快照信息显示查询结果,无需关心此时是否有其他事务在执行
- 通过
undo log
将数据回滚至历史某个版本,从而实现秒级创建数据库快照的特点 - 在
可重复读
事务隔离级别下,事务第一个读操作的时候,生成快照信息(一致性读视图) - 在
读提交
事务隔离级别下,事务中的每个读操作,都会重置快照信息(将当前快照更新为最新的数据视图)
可重复读级别下的MVCC实现
三点前提
-
每个事务在启动的时候,InnoDB会给事务分配一个事务ID(自增,唯一,类似主键)
-
InnoDB表的行记录存在两个隐藏行,事务ID列(row trx_id)和回滚指针列,
row trx_id
代表的实际上是数据的版本,在提交事务的时候,row trx_id
列会被赋值为提交的事务的事务ID -
事务在启动的时候,同时会设置事务的
up_limit_id
,up_limit_id
是用来表明,事务在执行过程中,能看到的数据版本的上限(也就是说,select某一行数据,判断事务应该看到那个版本的数据,应该根据当前行的row trx_id
<=up_limit_id
)
那么基于以上三点,可重复读级别下的MVCC的执行流程应该如下图
事务A和事务B分别将id = 1
的这一行更新为v = 2
和v = 3
那么在id = 1
的这行数据的当前row trx_id
(数据版本)等于4
此时,有一个事务C对id = 1
的这行执行读操作,事务C的up_limit_id = 3
,那么该事务读到的数据版本就应该是row trx_id = 2, v = 2
事务C查询id = 1
的那一行数据的时候,发现数据的版本row trx_id = 4
> 事务C的up_limit_id = 3
然后通过undo log
回滚到上一版本v = 2, row trx_id = 2
,此时再次比较,发现数据的版本row trx_id = 2
《 事务C的up_limit_id = 3
,表示数据可见!!
MVCC的数据快照实际上是通过当前的数据版本再加上回滚段,来实现这种轻量级的保存数据快照的方式
可重复读隔离级别下,长事务的危害
在长事务的执行过程中,随着其他事务的提交,数据的row trx_id
会不停增加,此时row trx_id
与长事务的up_limit_id
的差距会越来越大,这意味着,系统保留的undo log
的回滚段会越来越大
那么长事务中执行一条select语句的时候,InnoDB会根据当前版本的数据 + 大量的undo log
的回滚段得出该事务的一致性读视图,undo log
会占用大量的存储空间,同时select语句的 执行时间也会边长