MySQL——MVCC实现原理流程分析
目录
ReadView可见性规则
不同隔离级别下ReadView的生成时机
流程示例
读已提交级别
可重复读级别
前置知识:https://yjy66.blog.csdn.net/article/details/151332894?fromshare=blogdetail&sharetype=blogdetail&sharerId=151332894&sharerefer=PC&sharesource=m0_74386799&sharefrom=from_link
补充:
ReadView可见性规则
- 如果行记录的trx_id小于ReadView中的min_trx_id,则该记录对当前的事务可见
- 如果行记录的trx_id大于ReadView中的max_trx_id,则对当前事务不可见
- 如果行记录的trx_id在min_trx_id和max_trx_id之间,则检查trx_id是否在ReadView的trx_ids列表中。有两种情况:
1、如果m_ids包含trx_id,表示ReadView生成时该事务未提交。当trx_id等于creator_trx_id,表示数据是自己生成的,可见。否则不可见。
2、如过m_ids不包含trx_id,说明这个事务在ReadView生成之前就已经提交,可见。
不同隔离级别下ReadView的生成时机
- READ UNCOMMITTED(读未提交):不生成ReadView。
- READ COMMITTED(读已提交):每次执行SELECT时生成新的ReadView。
- REPEATABLE READ(可重复读,MySQL默认):事务第一次执行SELECT生成ReadView。
- SERIALIZABLE(串行化):不适应ReadView机制。
流程示例
- 获取事务自己事务id;
- 获取Read View
- 查询得到的数据,然后Read View中的事务版本号进行比较。
- 如果不符合可见性规则,需要Undo log历史快照。
- 最后返回符合规则的数据
读已提交级别
MySQL的默认隔离级别是可重复读,因此需要修改隔离级别,查询当前事务隔离级别的语句是(MySQL 8.0即以上版本):
SELECT @@transaction_isolation;
如果是低版本的命令为:
SELECT @@tx_isolation;
可以看到当前的级别都是RR,因此需要修改隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
可以看到成功改为读已提交。
当前初始数据:接下来开启两个事务。
经过一番操作后可以看到,事务A连续查询两次的结果不一样,接下来分析一下流程。
- 事务A开启,假设得到的事务id为10.
- B开启,得到的事务id为11.
- 事务A执行select,生成一个ReadView:
变量 值 m_ids(当前活跃事务id的集合) 10,11 min_trx_id(最小活跃事务id) 10 max_trx_id(预分配的事务id) 12 creator_trx_id(当前事务的id) 10 - 接下来根据可见性规则进行判断:在第一次select,行记录的trx_id为10,trx_id=creator_trx_id,所以可见,接下来事务B修改之后并提交,事务A再次查询,由于RC级别每次查询都会生成一个ReadView。
- 新的ReadView如下:
此时行记录的trx_id为11,接下来进行可见性判断:trx_id大于min_trx_id,小于max_trx_id,并且m_ids不包含11,所以可见。
变量 值 m_ids(当前活跃事务id的集合) 10 min_trx_id(最小活跃事务id) 10 max_trx_id(预分配的事务id) 12 creator_trx_id(当前事务的id) 10
可重复读级别
先把事务隔离级别改回RR:
- 事务A开启,假设得到的事务id为10.
- B开启,得到的事务id为11.
- 事务A执行select,生成一个ReadView:
变量 值 m_ids(当前活跃事务id的集合) 10,11 min_trx_id(最小活跃事务id) 10 max_trx_id(预分配的事务id) 12 creator_trx_id(当前事务的id) 10 - 接下来根据可见性规则进行判断:在第一次select,行记录的trx_id为10,trx_id=creator_trx_id,所以可见,接下来事务B修改之后并提交,事务A再次查询,RR级别不会再生成新的ReadView
- 此时trx_id大于min_trx_id,小于max_trx_id,并且包含在m_ids中,根据可见性规则不可见!