Mysql MVCC
MVCC Multi Version Concurrency Control ,多版本并发控制。主要为了提高数据库的并发性能。在对同一行数据同时进行读写时,一般会采取加锁的方式来保证一致性,但是这样多个请求会变为串行执行,极大降低Mysql的性能。为此采用MVCC的方式去处理读–写请求,做到在发生读写冲突的时候不用加锁,提高性能。
当前读和快照读
当前读:当前即是最新,顾名思义就是读取的最新版本,因为要读取最新版本,所以在读取的时候,其他事务不能修改,为此需要加锁,当前读常见的SQL如:
- select … lock in share mode : 共享锁
- select … for update : 独占锁
- insert,update,delete : 独占锁
快照读:读取的只是记录行的一个快照,不一定是最新数据。在非串行化的隔离级别中,不加锁的select操作均是快照读。如果是串行化的隔离级别的话,最终会退化为当前读。
MVCC的实现
MVCC多版本并发控制,基于数据版本进行并发访问。MVCC通过为每一个事务分配一个递增的事务id,每个id都会对应一个快照版本,
MVCC需要依靠版本链来实现快照读,在innodb行的内部有两个隐藏列,
- DB_TRX_ID : 存储插入或者更新语句的最后一个事务的事务ID,6byte
- DB_ROLL_PTR : 回滚指针,每次对记录进行修改,都会对应undo log,这个指针就会指向之前的undo log通过它可以找到之前的记录。
innodb的每一行的数据记录除去数据列之外,还额外保留了几个列。
假设有一个当前表中的记录如下所示:
紧接着,又一个新的事务暂且称为事务1,且该事务的事务id为1,将name修改为tom,此时的数据就成这样的
然后再有一个事务将age改为30,该事务的事务Id为2,此时的版本链是这样的:
也就是说,对于记录的每次更新,都会将其旧值链到新纪录的后面,通过回滚指针,链表头的记录就是当前最新的记录。
ReadView
ReadView称为当前事务的读视图,ReadView包含4个内容:
- m_ids : 生成ReadView时,当前系统中获取的未提交的事务id组成的集合。
- min_trx_id:m_ids中最小的事务id
- max_trx_id : mids中最大的事务id+1
- creator_trx_id:生成该ReadView的事务id
有了这些数据,在RR,RC隔离下,就可以很容易的判断出版本链中的哪条数据可以显示,哪条数据不能显示。只需要从链表头部依次向尾部查询,查询到第一条符合记录的数据返回即可。
1、如果被访问的版本的记录的事务id,等于ReadView中的creator_trx_id,则表明这条记录就是该事物修改的,因此可见。
2、如果被访问的版本的trx_id小于ReadView中的min_trx_id,表示生成该版本的事务在创建ReadView的时候,已经提交了,所以该版本可以读出来。
3、如果被访问版本的记录的事务Id,大于最大的事务id,则说明该记录的事务在之后才产生的,当前不可见。
4、如果被访问的记录介于这个集合中,需要判断是不是和集合中的值相等,如果相等说明当前这个事务还是活跃的,不可见。否则不相等则说明在生成readView的时候,该事务已经被提交了因此可见。
简而言之,对于一个视图的可见不可见总共一下几种情况:
- 自己修改的,自己可见,不论是否已经提交
- 版本未提交,不可见
- 版本已经提交,但是在开启事务视图之后提交的也不见
- 版本已经提交,且是在事务视图开启之前提交的可见
在RR隔离级别中,仅在第一次执行快照读的时候生成readView,后续都使用该ReadView,但是如果两次快照读之间有当前读则会重新生成ReadView。
在RC隔离级别中,每次都会重新生成ReadView。