MYSQL使用MVCC机制实现事务隔离,主要是通过构建一致性视图来实现事务可见性隔离。我常说问题解决总是伴随这新问题的产生,看看MVCC给我们带来了那些有趣的思考吧。

在MySQL中默认隔离级别是RR(REPEATABLE-READ),RR隔离级别增加了间隙锁,可以有效的避免幻读,阻止不可重复读。听起来是不是感觉万无一失了,其实不是这样的,我们在工作中事务的声明和加锁是分多个阶段执行的,这个很好理解。毕竟事务声明后,可能要执行查询更新等多个操作,总不能声明事务的时候就直接加锁吧,这个时候你也不知道为哪一行记录加锁啊。但是这样就造成了多个事务声明后,由于不可重复的的原因,后生成的事务对记录的变更对新事物不可见。通俗点说,A事务先于B事务声明,此时如果A事务对记录产生变更,那么B事务无法读取这个变更。到这里就有必要谈谈MYSQL事务隔离实现机制MVCC和一致性视图的问题了。

什么是MVCC 机制

MVCC是多版本并发控制,Multiversion concurrency control,引用一段淘宝数据库月报的解释如下:

多版本控制: 指的是一种提高并发的技术。最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。在内部实现中,与Postgres在数据行上实现多版本不同,InnoDB是在undolog中实现的,通过undolog可以找回数据的历史版本。找回的数据历史版本可以提供给用户读(按照隔离级别的定义,有些读请求只能看到比较老的数据版本),也可以在回滚的时候覆盖数据页上的数据。在InnoDB内部中,会记录一个全局的活跃读写事务数组,其主要用来判断事务的可见性。

从这段解释,我们可以看到MCVV不仅提高了高并发,而且可以找回历史版本。这一机制主要是依赖一致性视图的构建。MySQL 会在事务开始后建立一个一致性视图(并不是立刻建立),在这个视图中,会保存所有活跃的事务(还未提交的事务)。一致性视图解决不同事务之间可见性的问题,很通俗的大白话就是,记录只在生成视图前对事务可见。

听起来挺好,但是问题是MYSQL不阻塞事务声明提交,多个事务之间交叉声明,这就导致新务无法读取老事务对数据的更新。(如果事务可以串行执行就好了,不过这同时会带来并发行问题)。由于这个是MYSQL内部实现机制,因此我们无法通过编程来解决,只能通过将事务隔离级别改为RC,但是这同时也带来数据胀读问题。A事务更新记录,导致B事务多次读取记录的结果不一致,幸运的是我们可以通过代码解决数据脏读的问题。