多版本并发控制:

MVCC就是通过数据行的多个版本来管理实现数据库的并发控制,这项技术使得在InnoDB的事务隔离级别下执行一直性读操作有了保证,换言之就是为了查询一些正在被另一个事务更新的行,并且可以看到他们被更新之前的值,这样在做查询的时候就不用等待另一个事务释放锁

MVCC的实现依赖于:隐藏字段、Undo Log、ReadView

MySQL中只有InnoDB存储引擎支持MVCC

MySQL中默认的隔离级别是可重复读,解决了脏读和不可重复读的问题,但是MVCC可以不采用锁机制,而是通过乐观锁的方式来解决不可重复读和幻读的问题!

隐藏字段、Undo Log版本链:

trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务id复制给trx_id隐藏列

roll_pointer:每次对某条聚簇索引进行改动 时,都会把旧的版本写入undo日志中,这个隐藏列相当于一个指针,可通过它来找到该记录修改之前的信息

每次对记录进行改动,都会记录一条undo日志,每条undo日志也都有一个 roll_pointer 属性
( INSERT 操作对应的undo日志没有该属性,因为该记录并没有更早的版本),可以将这些 undo日志
都连起来,串成一个链表:

1646635959975.png

对该记录每次更新后,都会将旧值放到一条 undo日志 中,就算是该记录的一个旧版本,随着更新次数
的增多,所有的版本都会被 roll_pointer 属性连接成一个链表,我们把这个链表称之为 版本链 ,版
本链的头节点就是当前记录最新的值。
每个版本中还包含生成该版本时对应的 事务id 。

ReadView:(核心)

1.什么时ReadView?

在MVCC机制中,多个事务对同一个行记录进行更新会产生多个历史快照,这些历史快照保存在Undo Log中。如果一个事务想要查询这个行记录,需要读取哪个版本的行记录呢?这时就需要用到ReadView了,帮我们解决可见性的问题

ReadView就是一个事务在使用MVCC机制进行快照读取操作时产生的读视图,当事务启动时,会根据数据库系统当前的一个快照,InnoDB为每个事务构造了一个数组,用来记录并维护系统当前活跃事务的id(活跃就是启动了事务但是没有提交)

MVCC整体操作流程
  1. 首先获取事务自己的版本号,也就是事务 ID;
  2. 获取 ReadView;
  3. 查询得到的数据,然后与 ReadView 中的事务版本号进行比较;
  4. 如果不符合 ReadView 规则,就需要从 Undo Log 中获取历史快照;
  5. 最后返回符合规则的数据。

总结:

MVCC只有在读已提交和可重复读两种隔离级别的事务在执行快照读操作时访问记录版本链的过程,这样使不同事务的读-写、写-读操作并发执行,提升系统性能

核心点在于ReadView的原理,读已提交和可重复读这两个隔离级别的很大不同就是生成ReadView的时机不同:

  1. READ COMMITTD在每次进行普通的SELECT操作前都会生成一个ReadView
  2. REPEATABLE READ只会在第一次进行普通的SELECT操作前生成一个ReadView,之后的查询都重复使用这个ReadView

通过MVCC可以解决的问题:

1.读写之间阻塞的问。通过MVCC可以让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就可以提升事务并发处理能力

2.降低了死锁的概率。因为MVCC采用了乐观锁的方式,读取数据时并不需要加锁,对于写操作,也只锁定了必要的行

3.解决快照读的问题。当我们查询数据库在某个时间点的快照时,只能看到这个时间点之前事务提交更新的结果,而不能看到这个时间点之后事务提交的更新结果