MVCC是一种并发控制协议,我们经常会听到协议两个字,比如Tcp协议,HTTP协议,raft协议等等。感觉协议高深莫测,只有那些高手怪才才能理解。其实我对协议的理解很简单,协议就是为完成某项作业而指定的一系列约定!比如古时候士兵放哨,遇见敌情通过点火放烟的方式来传递信息,这就是简单的协议。只不过互联网那些协议搞得更加复杂更加严谨罢了。

我们再来看看MVCC协议。MVCC的全称是Multi-Version Concurrecy Control,即多版本并发控制协议。它解决来我们什么问题呢?

我们都知道SQL标准中定义了四种隔离级别 。MySQL数据库完整地实现了这四种隔离级别(并不是所有的数据库厂商都这么听话),这其中MVCC 功不可没。当然MVCC并不是实现这些隔离级别的必要条件,但若没有MVCC,读写的隔离就需要加锁,数据库的性能会非常差,高并发更是无从谈起。因此MVCC使得读操作不再影响写操作,读写可以并发执行,同时又保障来事务的隔离级别。这使得MVCC是所有事务型数据库中必不可少的一部分。

那么数据库又是这么实现的呢?

在InnoDB引擎中,每一行都有三个隐藏的内部属性:

  1. DB_TRX_ID:占6个字节,记录操作改行数据的最近的一个事务ID,事务的ID只增不减,因此数据的版本也是由它来体现的。
  2. DB_ROLL_PTR:占7个字节,指向回滚段中的回滚记录。如果该事务还没有commit, 这个字段的值确保能够准确找到回滚记录。
  3. DELETE_BIT: 标记该记录是否被删除

这三个字段是实现MVCC协议的关键!

在MVCC协议下,数据库增删改查通过如下方式实现:

  • INSERT
    插入一条数据时,会将插入改行的事务ID(trx_id)保存保存到隐藏的属性中.
  • SELECT
    这个查询比较麻烦,一条数据对于改事务是否可见,InnoDB要从两方面进行判断:
    1. 是从事务版本号来判断,该事务只能读到比该事务更小版本号(trx_id)的数据.
    2. 还要判断数据是否被删除。即数据行的删除位要么为定义,要么定义了,但版本号(trx_id)大于当前事务。也就是该数据被删除,但是被当前事务之后的事务删除的,当前事务还是可见的。
      理解上述这两点很重要!
  • DELETE
    删除操作比较简单,mark一下数据行的删除标记位,并更新该数据的版本号,数据并没有真正删除。真正删除是由purge线程定时操作的,当purge线程发现某条版本数据不再被当前任何事务所使用时,就把它purge掉了。
  • UPDATE
    更新操作是由INSERT和DELETE两个操作来完成的。即把旧的数据记录标记删除,并更新版本号为最新的trx_id。同时插入一条新的数据,新的数据是更新后的数据,并保存当前版本号。因此即使你更新一个字段,InnoDB内部也要插入整行的数据。

由上述可以看的,我们所熟悉的增删改查,数据库底层的操作并不是那么简单。因为它即要兼顾事务的ACID,又不能丧失高并发。MVCC就是这样应运而生!