前置知识
涉及到的几个概念:隐藏字段,undo log,readview
- (每个表中的)隐藏字段:最后修改记录的事务id,回滚指针
- undo log :在插入/更新数据的时候记录回滚日志
- 当前读:读取的是记录的最新版本,在执行的时候会加锁,防止其他并发事务修改该记录
select ... for update、update、insert、delete(排他锁)都是一种当前读 - 快照读:读取的可能是记录的可见版本,可能是历史记录
对MVCC理解:
实现了事务隔离
每次开启事务都会创建一个 read view ,以及回滚日志 undo log,从而会形成一条回滚链
关闭事务那么 read view 也会被关闭
回滚日志何时被清除?
如果 read-view A 被关闭了,且 '将2改成1' 这个回滚日志之前没有 read-view 了,那么此回滚日志就可以被删除
分析利用MVCC特性读取同一个记录的不同版本
对于同一个记录,如果对它开启了多次事务,就会产生如图中的多个快照读 A,B,C。
假设事务A,B,C分别对应于上述快照读。由图可知当前值为4,如果事务 A 需要读取该记录值,那么需要由当前值 依次回滚 从而得到 1 这个值。
分析事务是否是隔离的
事务的隔离,指的就是多个事务的执行不会互相影响。具体点说,在 RR 隔离级别下,事务 A 在开启后,多次读取同一个记录,读取到的值始终是一致的。
从这个引出了一致性读 (read-view) 这个概念。能够保证事务 A 上述运行效果,依靠的是 MVCC (多版本并发控制) 这个概念。MVCC 通过 read-view,row_trx_id,回滚日志,回滚链等保证了前后读取的一致性,这是对于一个事务中只有查询操作而言的。
在事务 A 开启后,其实对整个库进行了一次快照,但是这个快照并不是真正的整个数据库数据,这太庞大了。而是由 活跃事务数组 来保证对全库的快照。
活跃事务是指当前未提交的所有事务,通过这么一个活跃数组,就达到了开启事务后对全库数据进行快照的效果。
从这个角度来看,事务是隔离的。因为这是站在只有查询语句的视角。
而从下面这个视角来看,事务是不隔离的。因为有当前读这个概念。
有上述两个事务,初始数据 (id,k):(1,1),开启事务 B
事务 C 更新后变成(1,2)
此时事务 B 才执行更新,而更新操作需要读取此记录的 当前值,也就是事务 B 读取到了 (1,2),然后做更新;而后再次查询,会发现(1,3)这种情况。
这样看事务好像是非隔离的,因为事务 B 是先于事务 C 开启的,它理应读到 (1,1) 这样的数据然后去做修改。
但是 MySQL 要保证不能丢失事务 C 的修改,且它已经提交了。所以可以得知:对于更新操作,是先读后写的,而且这个读是当前读(即该记录的最新版本值),并且要获取行写锁 ( X )。
文章评论