InnoDB 一共支持四种等级的事务:
- 读未提交
- 读以提交
- 可重复读
- 串行化
其中读未提交实现最简单,每次读最新的记录即可。而串行化则是通过加锁完成的。其中读已提交和读未提交是通过 MVCC 实现的,其基本原理都相同。
版本链
对于使用InnoDB存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列:
- trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务 id 赋值给 trx_id 隐藏列。
- roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。
同样,undo log 中也存在 trx_id 以及 roll_pointer 指针。roll_pointer 指向该 undo log 上一个版本的 undo log。因此修改的记录根据 roll_pointer 就形成了链表,这条链表中的所有值就是形成了版本链。
ReadView
ReadView 是实现读以提交和可重复度的实现方式。其实现原理,则是对于查询的字段,寻找其版本链上对当前 ReadView 的版本。例如,读已提交隔离等级下,未提交的事务所修改产生版对当前 ReadView 就是就是不可见的。
ReadView 中包含四个比较重要的概念:
- m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
- min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
- max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。
小贴士: 注意max_trx_id并不是m_ids中的最大值,事务id是递增分配的。比方说现在有id为1,2,3这三个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时,m_ids就包括1和2,min_trx_id的值就是1,max_trx_id的值就是4。 - creator_trx_id:表示生成该ReadView的事务的事务id。
有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:
- 如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
- 如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
- 如果被访问版本的trx_id属性值大于或等于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
- 如果被访问版本的trx_id属性值在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。
读已提交以及可重复读实现的方式只是生成 ReadView 时机不同:
- 读已提交: 每次 select 时都会生成 ReadView
- 可重复读: 只在事务开启时生成一次 ReadView
网友评论