MVCC
MVCC介绍
mvcc是mysql为了解决
脏读
、不可重复读
等事务之间读写问题而诞生的;它替代了一些场景下的低效锁,在保证隔离性的基础上,提升了读取效率和并发性。MVCC实现
在mysql中mvcc是基于mysql的undo log和readview来实现的。
MVCC(Multi-Version Concurrency Control)中文叫做多版本并发控制协议,是 MySQLInnoDB 引擎用于控制数据并发访问的协议。它在面试中属于必面题,而且从 MVCC 出发能够将话题引申到事务、隔离级别两个重头戏上,所以掌握 MVCC 能让你进可攻退可守。
如果是纯粹的锁,那么写和写、读和写、读和读之间都是互斥
的。如果是读写锁,那么写和写、读和写之间依旧是互斥的。数据库和一般的应用有一个很大的区别,就是数据库即便是读,也不能被写阻塞住。
Mysql事务隔离级别脏读是指读到了别的事务还没有提交的数据。之所以叫做“脏”读,就是因为未提交数据可能会被回滚掉。
不可重复读是指在一个事务执行过程中,对同一行数据读到的结果不同。
幻读是指在事务执行过程中,别的事务插入了新的数据并且提交了,然后事务在后续步骤中读到了这个新的数据
快照读和当前读。简单来说,快照读就是在事务开始的时候创建了一个数据的快照,在整个事务过程中都读这个快照;
而当前读,则是每次都去读最新数据。MySQL 在可重复读这个隔离级别下,查询的执行效果和快照读非常接近。
undo log
在innodb中,每一条sql通过执行器后,就会在回滚段undolog segment中申请一个undo log页,根据sql信息来构建对应的undo log内容,同时将其写入磁盘,以保证每次操作真正数据之前undo log是完整的,之后才会执行数据写入记录redo log等。
undo log格式
上图可以看到,每条undo log日志中会包含旧roll_pointer,这样就方便mysql查询到之前的历史数据记录,而想知道mysql如何应用好这些版本记录,还需要了解一下innodb引擎数据行的格式,在innodb引擎中默认的格式为DYNAMIC,它的结构如下:
可以看到在每一行中都有一个字段来记录回滚指针,而该回滚指针就会指向undo log,如下图:
这样通过roll_pointer关联,就把undo log组成一个链了,回滚时只需要依次撤销即可。
Read View
Read View 只用于已提交读和可重复读 两个隔离级别,它用于这两个隔离级别的不同点就在于 什么时候生成 Read View。
- 已提交读:事务每次发起查询的时候,都会重新创建一个新的 Read View。
- 可重复读:事务开始的时候,创建出 Read View。
在进行数据查询时,mysql会构造一个read view,它会记录该数据版本链的一些统计值例:
1.m_ids:当前活跃的事务集合(还未提交的事务)
2.min_trx_id:集合中最小事务编号(版本链结尾的事务id)
3.max_trx_id:集合中最大事务编号 + 1
4.creator_trx_id:创建当前视图的事务编号
构建完read view后根据以下规则查询即可:
- 判断当前版本数据是否为当前事务创建(creator_trx_id = trx_id),意味着读取自己修改的数据,是可以直接访问的;
- 如果当前版本数据的事务编号小于最小活跃事务(min_trx_id > trx_id),意味着该版本已经提交可以直接访问;
- 如果当前版本数据大于最大事务编号,则意味着创建该版本数据时还有生成该事务,需要遍历undo log,直到版本数据小于max_trx_id或者trx_id为空没有数据为止;
- 当前版本数据的事务编号在min_trx_id和max_trx_id之间,同时不在活跃事务列表中,意味着创建事务时该版本数据已经提交可以查询到,如果不是则继续遍历undo log.
通过以上规则进行查询,就可以实现查询到的都是已经提交事务的数据,解决了
脏读
问题。在事务第一个查询时创建一个Read View,后续查询都用这个Read View进行判断,这样每次查询结果都是一样的,这样就解决了不可重复读
问题;在可重复读的隔离级别下就采用这种方式来解决不可重复读
问题;如果事务每次查询都创建一个Read View,这样就会有不可重复读
问题,在读已提交的事务隔离级别下就是这种实现方法。Loading...