天天看点

透彻解读MVCC

一.MVCC是什么?

     MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问.

为什么要有MVCC?

      在并发访问场景中,会出现以下三种情况

      读读、读写、写写

      读读不存在安全问题,写写必然需要加锁来保证数据安全,那MVCC其实就是大佬们不满意简单加锁来解决读写冲突的一种无锁手段.

MVCC本质是什么?

      其实就是通过一些手段(隐藏字段,日志,readview)和算法来判断事务间的可见性,从而解决脏读,不可重复读数据问题.

二.前置知识

在介绍MVCC之前,我们应该先清楚以下知识点.

1.并发访问可能带来的数据问题.

      (1)脏读:在一个事务中读取了另一个事务尚未提交的数据.

      (2)不可重复读:在一个事务中两次读取数据内容不一致,主要由update引起.

      (3)幻读:在一个事务中的两次读取数据数量不一致,主要由insert和delete引起.

2.读数据的两种方式.

(1)当前读:

      读取的是记录数据的最新版本,并且当前读返回的记录都会加上锁,保证其他事务不会再并发的修改这条记录

      以前方式均为当前读:

      select … lock in share mode(会增加表锁)

      select … for update(会增加行锁)

      insert

      update

      delete

(2)快照读:

      读取的是记录数据的可见版本(可能不是最新的数据),不用加锁

以下方式为快照读:

      简单的select操作(不包括 select … lock in share mode, select … for update)

3.数据库的隔离级别:

透彻解读MVCC

mysql中默认的隔离级别是RR,也就是可重复读.

三.MVCC组成.

MVCC由隐藏字段,undolog,readview三部分组成.

1.隐藏字段.

(1)DB_TRX_ID

      创建这条记录的事务id或者最后一次修改的事务id.

(2)DB_ROLL_PTR

      回滚指针,指向这条记录的上一个版本.

(3)DB_ROW_ID

      隐藏主键,如果没有主键会生成一个6字节的row_id.

举例:有一个user表,表中有三个字段 name,age,gender,现在新增一条name=zhangsan的记录,数据情况如下:

透彻解读MVCC

2.undolog

回滚日志,记录事务中的历史数据,便于在insert,update,delete操作之后进行回滚.

举例:还是上面那个表,现在将name=zhangsan改成name=lisi,那我们数据库中的数据则变为.

透彻解读MVCC

而历史数据则会存储到undolog日志中去,回滚指针会指向该数据存储位置.

注意:在undolog日志中会存储所有的历史数据,那岂不是会无限增长?所以会有一个单独的线程去回收清除不需要的日志

3.readview

     事务在进行快照读的时候产生的一个读视图.所谓的读视图只是包含了某些字段信息而已.

作用:

      其实就是为了进行判断多个事务间的可见性判断而存在的信息记录.

      readview包含以下三个字段信息.:

      (1)trx_list:

            一个数值列表,用来记录当前readview生成时刻存在的活跃事务(也就是尚未提交的事务)id.

      (2)up_limit_id:

            事务活跃列表中最小的事务id.

      (3)low_limit_id:

            在readview生成时刻尚未分配的下一个事务id.

重点:

      在不同的隔离级别(RC和RR)下生成的readview的时机是不一样的.

      RC:每次进行快照读都会生成新的readview.

      RR:事务开启后第一次进行快照读的时候才会生成readview,以后的快照读都会使用第一次生成的readview(如果进行当前读,则会重新生成readview).

四.MVCC核心流程.

首先给一个案例,在RR隔离级别的情况下.

     在我进行如下操作的时候,事务t2在进行快照读的时候能看到t4更新的数据.

透彻解读MVCC

     但是当我修改事务操作顺序,如下图所示,那么t2在第二次快照读就看不到t4更新的数据.

透彻解读MVCC

     究竟是为什么呢?下面用mvcc进行刨析,相信看完就会理解.

首先在第一种情况下:

透彻解读MVCC

      t2在进行快照读的时候,会生成一个readview,而是否能看到个t4事务的个更新数据取决于可见性算法(下图黄框中所示).依次执行1,2,3判断逻辑.可知在t2进行快照读的时候是能够看见t4更新的数据的.

透彻解读MVCC

第二种情况:

透彻解读MVCC

      首先在t2进行第一次快照读的时候,生成的readview如下所示. 这里也显示出DB_TRX_ID的值,因为一会要用到.

透彻解读MVCC

      然后在t4进行事务提交后,会修改这条记录中DB_TRX_ID的值,修改为4

透彻解读MVCC

      那么在t2进行第二次进行快照读的时候,因为此时的隔离级别是RR,所以不会生成新的readview,会使用第一次生成的readview,此时继续用可见性算法进行判断,由第三个逻辑判断可知,在readview生成时刻,t4事务还是活跃状态,还没有提交,那么此时当前数据是看不到的,也就验证了前面的观点.

透彻解读MVCC

注意:

     在快照读的时候有时候会读的是历史数据,读的是哪里的历史数据?

      是undolog日志中的历史数据

五.如何解决幻读.

我们知道MVCC可以解决脏读和不可重复读,那幻读要怎么解决呢?

1.怎样才会产生幻读?

      在快照读和当前读混合使用的时候才会产生幻读,也就是说当事务中的读操作全部使用快照读的时候是不会产生幻读的.

为什么?

      因为在全部使用快照读的时候都读的第一个readview生成的历史数据,而当进行了当前读,就会重新生成readview,导致出现幻读.

2.解决方式.

在第一次查询的时候就使用当前读,会对数据加上行锁和间隙锁,这样其他事务在进行插入的时候会阻塞,从而解决幻读.