MySQL 中你必须要懂的 MVCC( 二 )


READ UNCOMMITTED该隔离级别不会使用 MVCC 。它只要执行 select,那么就会获取 B+ 树上最新的记录 。而不管该记录的事务是否已经提交 。
READ COMMITTED在 READ COMMITTED 隔离级别下,会使用 MVCC 。在开启一个读取事务之后,它会在每一个 select 操作之前都生成一个 Read View 。
因为步骤 2 中的 select 读取时,没有活跃的事务,也就表明所有的事务都是已经提交了的 。所以它能读取到第一条记录 。
执行步骤 3,开启一个新的事务,事务 id 为 101(以下检测事务 101) 。
执行步骤 4,它修改了 id 为 1 的记录,此时版本链将会变为如下:

MySQL 中你必须要懂的 MVCC

文章插图
 
执行步骤 5,事务 0 执行了一个 select 操作,事务 0 会生成一个 Read View 。Read View 的值如下:
MySQL 中你必须要懂的 MVCC

文章插图
 
我们根据上面对版本链中的记录可见性规则:
  1. 版本链中的第一条记录,它的 trx_id 不小于 min_trx_id,所以该记录不可见 。
  2. 版本链中的第二条记录,它的 trx_id 小于 min_trx_id,所以该记录可见 。
所以对于此次的查询,它能获得的记录就是:
MySQL 中你必须要懂的 MVCC

文章插图
 
事务 101 在步骤 5 中执行了一个更新操作,执行步骤 6,提交该事务之后,版本链如下:
MySQL 中你必须要懂的 MVCC

文章插图
 
执行步骤 7,我们在事务 0 中执行一次 select 查询,因为我们的隔离级别是 READ COMMITTED,所以此次查询也会生成一个 Read View 。该 Read View 的值如下:
MySQL 中你必须要懂的 MVCC

文章插图
 
然后根据版本链可见性规则:
  • 因为没有活跃的事务,可知所有事务都已经提交,所以 rw_trx_ids 为空 。
  • 版本链第 1 条记录,它的 trx_id 小于 min_trx_id,所以此记录可见 。
那么这次的查询可以得到的记录如下所示:
MySQL 中你必须要懂的 MVCC

文章插图
 
在事务 0 执行完查询之后,我们又开启了一个事务 id 为 102 的新事务(以下简称事务 102),该事务也对 id 为 1的记录进行了更新 。
步骤 9 中的查询自行分析 。我们直接给出事务 102 执行完两条更新语句的最终版本链:
MySQL 中你必须要懂的 MVCC

文章插图
 
执行步骤 11,根据版本链可见性规则,它能获取到的记录:
MySQL 中你必须要懂的 MVCC

文章插图
 
REPEATABLE READ实际上,REPEATABLE READ 与 READ COMMITTED 的区别只有在生成 Read View 的时机上 。
READ COMMITTED 是在每次执行 select 操作时,都会生成一个新的 Read View 。而 REPEATABLE READ 只会在第一次执行 select 操作时生成一个 Read View,直到该事务提交之前,所有的 select 操作都是使用第一次生成的 Read View 。
我们重新执行一下表中的步骤 。
首先,执行到步骤 2,事务 0 开启了事务之后,并执行一次 select 查询 。此时会生成一个 Read View 。该 Read View 的结构如下:
MySQL 中你必须要懂的 MVCC

文章插图
 
生成的 Read View 将会一直使用,直到事务 0 提交 。
所以,尽管后面的开启了两个事务,并且对记录进行修改,使得最终的版本链变为如下所示:
MySQL 中你必须要懂的 MVCC

文章插图
 
但是事务 0 依然只能读取到最开始的那条记录,也就是:
MySQL 中你必须要懂的 MVCC

文章插图
 
不管事务 0 在任何时候执行 select * from person where id = 1; 读取记录,那么它都只会使用第一次生成的 Read View 在版本链中选择可以读取的记录 。
SERIALIZABLE该隔离级别不会使用 MVCC 。如果使用的是普通的 select 语句,它会在该语句后面加上 lock in share mode,变为一致性锁定读 。假设一个事务读取一条记录,其他事务对该记录的更改都会被阻塞 。假设一个事务在更改一条记录,其他事务对该记录的读取都会被阻塞 。
在该隔离级别下,读写操作变为了串行操作 。
总结通过上面的文章,我们可以知道在 READ COMMITTED 和 REPEATABLE READ 隔离等级之下才会使用 MVCC 。


推荐阅读