在并发事务中,为了解决脏读、幻读、不可重复读的问题,实现的解决方案,用于提供事务隔离。
对于「读提交」和「可重复读」隔离级别的事务来说,它们是通过 Read View 来实现的,它们的区别在于创建 Read View 的时机不同。
下面讲解InnoDB中的MVCC实现机制
1.快照读与当前读
快照读:一致性读,读取快照数据,基于多版本,那么读取的不一定是最新版本
当前读:读取的最新版本的数据
2.基础概念解释
2.1隔离级别
在MySQL中,默认的隔离级别是可重复读,可以解决脏读和不可重复读的问题。如果按照隔离级别而言不能解决幻读问题,但是串行化会大量的减少数据库的事务并发能力。
所以使用了MVCC,通过乐观锁的方式解决了不可重复读和幻读行为,降低了系统的开销。
2.2 隐藏字段、Undo Log版本链
回顾一下undo日志的版本链,对于使用 InnoDB 存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列。
trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务id赋值给trx_id 隐藏列。
roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到 undo日志 中,然
后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。
undoLog版本链:每次对记录进行改动,会记录一条undo日志,将这些undo日志串联起来成一个链表,版本链中还包括生成该版本时对应的事务id。
3.MVCC实现原理
MVCC:实现依赖于隐藏字段,Undo log,Read View
3.1Read View
readView:多个事务对同一行记录进行更新会产生多个历史快照,这些都会放进UndoLog中,如果一个事务要查询这个行记录,readView来帮助读取哪个版本的行记录。
Read View:事务A在MVCC机制进行快照读操作的时候产生的读视图,当事务启动时,会生成数据库系统当前的一个快照,InnoDB为每个事务提供了一个数组,用来记录系统当前活跃事务的ID。
活跃事务:启动了但是还没提交
readView中将trx_id事务id分为这几种情况。
- 如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建 Read View前已经提交的事务生成的,所以该版本的记录对当前事务可见。
- 如果记录的 trx_id 值大于等于 Read View 中的
max_trx_id 值,表示这个版本的记录是在创建 Read View 后才启动的事务生成的,所以该版本的记录对当前事务不可见。 - 如果记录的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之间,需要判断 trx_id是否在m_ids 列表中:
-
如果记录的 trx_id 在m_ids列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见。
如果记录的trx_id 不在m_ids列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见。
3.2MVCC整体操作流程
- 首先获取事务自己的版本号,也就是事务 ID;
- 获取 ReadView;
- 查询得到的数据,然后与 ReadView 中的事务版本号进行比较;
- 如果不符合 ReadView 规则,就需要从 Undo Log 中获取历史快照;
- 最后返回符合规则的数据。
3.3可重复读和读已提交的区别
读已提交(Read Commited)时:每一次SELECT都会重新获得一次ReadView,那么活跃事务已经更改但是没有提交的部分,都会被发现,可能出现不可重复读和幻读问题。
可重复读(Repeatable read)时:一个事务只在第一次SELECT获得一次ReadView,而后面所有的 SELECT 都会复用这个 Read View。能够有效避免幻读和不可重复读部分。
4问题
4.1.幻读问题是怎么解决的
幻读是指在一个事务执行两次相同的查询,第二次查询返回了新的数据行,它与第一次查询返回的数据行不同,这种现象就称为幻读。
在InnoDB中,通过MVCC机制的间隙锁来解决幻读问题。当一个事务执行SELECT语句时,InnoDB会对查询的每一行记录加上共享锁,如果查询语句中使用了范围条件而不是相等条件,InnoDB会对查询范围内的所有行记录加上间隙锁。其他事务如果要插入一个新的记录,或者更新或删除一个已有的记录,都需要先获取到这个间隙锁,从而避免了幻读的问题。
4.2.了解MVCC吗
全称,用处—如何实现—可重复读和读已提交的实现差异
1.MVCC是指多版本的并发控制,用于实现可重复读和读已提交,用于提供事务隔离
2.MVCC主要实现依赖于隐藏字段,Undo log,Read View。
针对每一行字段会有两个隐藏字段,是指针域roll_ptr,undo log是把这些undo日志串联起来形成一个链表叫版本链。Read View是事务启动的时候数据库系统当前的快照,InnoDB提供了一个数组,记录当前活跃事务的id
3.MVCC在可重复读和读已提交的实现差异
在可重复读级别一个事务仅仅会在第一次select获得一次readView,这样后面的Select都会用同一个view,这样能有效避免幻读问题。
在读已提交级别每次select都会获得一个readView,这样其他活跃事务更新数据可能会影响当前事务,出现幻读问题。