1. 版本链
隐藏列
对于InnoDB存储引擎的表来说,它的聚簇索引记录中都包含以下两个必要的隐藏列:
- trx_id:一个事务每次对某条聚簇索引的记录进行改变的时候,都会把该事务的事务id赋值给trx_id。
- roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧版本写入到undo log中。这个隐藏列相当于一个指针,可以通过它找到该记录之前版本的信息。
版本链
每对记录进行一次改动,都会记录一条undo log。 每条undo log 都有一个 roll_pointer属性,通过这个属性可以将这条记录的undo log串成一个链表,这个链表称为版本链。这个版本链,可以用来控制并发事务访问相同记录时的行为。
2. ReadView
对于READ UNCOMMITTED隔离级别的事务来说,由于可以读到未提交事务修改过的记录,所以直接读取最新版本的记录即可。而对于使用SERIALIZABLE隔离级别的事务,都是串行化执行的,不需要考虑。
对于READ COMMITED 和 REPEATABLE READ 这两种隔离级别的事务,都必须保证读取到已提交的事务修改过的记录,所以只要判断版本链中的哪些版本是当前事务可见的即可。ReadView就是用来解决该问题的。ReadView主要包括以下四部分内容:
- m_ids:在生成ReadView时,当前系统中活跃的事务id列表。
- min_trx_id:在生成ReadView时,当前系统中活跃的事务中最小的事务id,即m_ids里最小的。
- max_trx_id:在生成ReadView时,系统应该分配给下一个事务的事务id。
- creator_trx_id:生成ReadView的事务的事务id。
有了ReadView之后,就能按以下步骤来判断版本链中的哪个版本对当前事务可见:
- 判断被访问的版本的trx_id与creator_trx_id是否一致,如果一致,则当前事务在访问自己修改过的记录,可以访问。
- 判断被访问的版本的trx_id是否小于min_trx_id,小于则表示该版本在ReadView生成之前已提交,可以访问。
- 判断被访问的版本的trx_id是否大于等于max_trx_id,是则表示该版本在ReadView生成之后再开启,不可以访问。
- 如果被访问的版本的trx_id在min_trx_id和max_trx_id之间,则需要判断trx_id是否在m_ids列表中,在则不可以访问,不在则可以访问。
如果某个版本的数据对当前事务不可见,则可以顺着版本链找到上一个版本,重复上面的四个步骤,如果最后一个版本都不可见,那么该条记录就对当前事务不可见。
READ COMMITED 和 REPEATABLE READ 生成ReadView的区别:
- READ COMMITED,每次读取数据前都生成一个ReadView。
- REPEATABLE READ,在第一次读取数据时生成一个ReadView。
