本文主要基于《高性能MySQL》
MVCC是多版本并发控制的缩写,其只在提交读和可重复读两个级别下起作用。MVCC使得大部分读操作都是非阻塞读,读写操作可以做到并行,比如:
- 一个事务在更新,另一个事务可以正常查询,即使是被更新的记录也可以,但是不能在进行更新,因为更新锁记录了;
- 一个事务正在查询,另一个事务可以正常的进行更新。包括查询的是全表,另一个事务也可以更新。删除也是一样。
这对提升读操作的并发性非常有帮助。
其原理是在每个记录行上增加两个隐藏列:
- trx_id:当某条记录被修改的时候,会将该事务的id赋值给修改后记录的trx_id隐藏列;
- roll_pointer:当记录被修改的时候,旧版本会写在undo log中,这个隐藏列就会记录指向旧版本的指针,通过它来找到之前的信息。
所有的版本都会被roll_pointer属性连接成一个链表,这个链表叫做版本链,头节点代表的当前记录最新值。
每当事务开始时,InnoDB便会创建一个ReadView,ReadView中主要包含3个比较重要的内容:
- m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
- min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
- max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值,max_trx_id并不是m_ids中的最大值。
当有一个事务查询时,mysql从版本链上的最新记录开始遍历,将版本链中每个记录的trx_id与上面提到的三个值进行比较,以确定查询结果:
- 如果trx_id大于max_trx_id,说明在当前事务之后又开启了一个新事务,并且新事物对该记录做了修改,那么这个改动对当前查询事务是不可见的;
- 如果trx_id小于min_trx_id,说明该版本对应的事务已经提交,那么该版本记录是可见的;
- 如果trx_id在m_ids列表中,说明该版本对应的事务还未提交,那么该版本记录是不可见的。
提交读隔离级别下,每次查询开始时都会生成一个独立的ReadView;而可重复读在第一次读取数据时生成一个ReadView,在事务的整个生命周期中不变化。这样通过ReadView的不同,满足了两个隔离级别对提交数据的不同要求。