innnodb实现MVCC背景
MVCC全程Muti-version concurrent control,innodb实现了MVCC,myisam没有实现MVCC,MVCC在innodb中主要通过undo log记录每条数据的版本链实现。MVCC避免在步加锁的情况下,结合read view解决了并发读写下出现的脏读和不可重复读 ,提高了mysql吞吐量。
在执行update/delete/insert跟更新操作时,会对应生成update/insert/delete相反的更新undo log日志。undo log日志中记录了每条记录被不同事务更新前的版本,undo log 实现每条记录的版本链如下图。
每个开启的事务都会分配全局唯一的id,该Id由一个全局自增的max_trx_id维护着,每当需要一个事务trx_id,会取max_trx_id,然后将max_trx_id加1。
delete undo log/insert undo log 日志在记录插入时创建,insert在出插入语句的事务提交时删除。update undo log在记录更新(包含delete、update)时生成,在涉及需要保证读取一致性的的事务时才会被删除。例如有事务A和事务B,事务B删除记录1的undo log 日志并没有在t2时刻删除,因为事务A还需要在t6时刻读取该条记录,为了保证读一致性,在t7时刻才事务B删除记录1的ungo log 日志。
MVCC实现原理
innodb在执行快照读的时候会生成快照readView,ReadView包含m_ids、low_limit_id、up_limit_id和creator_rtx_id四个字段,通过将每条记录版本链上中每个版本的事务的ID和readView四个字段、按照一定规则进行比较,判断该版本在该read view是否可见,可见,直接取出作为要读取记录值;不可见,沿着版本链遍历比较,直到找到可见的版本或者版本链遍历结束。
m_ids:创建当前readView时,系统中存活(未提交)事务id集合。
low_limit_id:创建当前readView时,系统中存活的最大事务id。***low_limit_id = min(m_ids)
up_limit_id:创建当前readView时,系统中存活的最小事务ID+1;***up_limit_id = max(m_ids)
creator_rtx_id:创建当前readView所在事务id。
版本链上版本可见性判断原则:
1、rtx_id<up_limit_id,说明该数据版本在该readView创建之前已经提交,数据可见;
2、 rtx_id >= low_limit_id,说明该数据版本在创建该readView之后创建,不应该访问;
3、 up_limit_id < trx_id < low_limit_id该版本数据可见。
3.1 rtx_id in m_ids && rtx_id == creator_rtx_id,说明该数据版本虽然没有提交,但是当前事务创建的,可以访问。
3.2 rtx_id in m_ids && rtx_id != creator_rtx_id,说明该数据版本没有提交并且不是当前事务创建的,不可以访问。
3.3 rtx 不在m_ids,说明该数据版本的事务在readView创建时已经提交,可以访问。
MVCC应用
innodb中通过快照读readView+undo log实现了read commited中的读已提交和read repeatable中的可重复读操作。
保证RC级别避免脏读
RC级别下可以读取的数据有其它事务已提交的数据和当前事务更新的数据,通过readView中四个字段判断和可见性原则从undo log版本链中筛选出来已提交数据和当前事务更新的数据。
MVCC保证RR级别下避免脏读和保证可重复度
RU级别下,每次查询数据会生成一个新的readView,所以无法保证可重复读,RR级别下,在创建事务的时候,使用trx_assign_read_view函数会创建一个global read view,每次查询都使用同一个read view,保证了可重复读。可见性判断规则和RC级别一样。
Reference:
https://blog.csdn.net/g6U8W7p06dCO99fQ3/article/details/120695033