MySQL MVCC详解

为什么需要MVCC

在没有MVCC之前,是使用读写锁(共享锁/排它锁)来进行并发控制的,读锁和读锁之间不互斥,写锁和读锁互斥,写锁和写锁互斥。
但是频繁加锁会导致数据库性能低下,这时出现了一种不加锁来解决读写冲突的方法,它会让数据库维护每条数据的多个版本,让不同的事务看到特定版本的数据,这个方法就是MVCC。

什么是MVCC

MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种用来解决读写冲突的无锁并发控制的方法,可以提高数据库并发读写的性能。

什么是快照读和当前读

快照读

不加锁的select操作是快照读,快照读读到的数据可能是之前的历史版本。快照读的实现是基于MVCC。

当前读

像select lock in share mode(共享锁),select for update,update,insert,delete(排它锁)这些操作都是当前读,它读取的是记录的最新版本,读取时会对这些记录加锁,保证其他并发事务不能修改这些记录。

MVCC的实现原理

MVCC是通过隐式字段,undo log和read view来实现的。

隐式字段

InnoDB在表中每行记录的后面,都隐式记录了几个隐藏的字段:

  1. DB_ROW_ID:自增ID,如果数据表没有主键,InnoDB会以DB_ROW_ID来生成聚簇索引
  2. DB_TRX_ID:最近插入/修改事务ID,记录创建该记录/最后一次修改该记录的事务ID
  3. DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本
    MySQL MVCC详解_第1张图片

Undo Log

undo log就是为了实现回滚操作而记录的日志。undo log分为两种,分别为:

  1. insert undo log:在插入数据时产生,会记录这条数据的主键值,回滚时会将这个主键值对应的记录删掉。只在事务回滚时需要,所以在事务提交后会被立即丢弃。
  2. update undo log:在更新或删除时产生,会把修改这条记录前的旧值都记录下来,回滚时会将这个记录更新为旧值。不仅在事务回滚时需要,在快照读时也需要,所以不能随便删除;只有当日志不再会被用到时,才会被purge线程清除。

在MVCC中用到的是update undo log,它的结构如下:
MySQL MVCC详解_第2张图片

Read View

read view就是事务进行快照读操作的时候生成的读视图(并不是每次快照读都会生成),read view主要包含以下四个属性:

  1. trx_list:活跃的事务ID列表;
  2. creator_trx_id:当前事务ID;
  3. up_limit_id:记录trx_list中最小的事务ID;
  4. low_limit_id:尚未分配的下一个事务ID。

当读取一条数据时,会先将这条数据的最新记录的DB_TRX_ID取出来,和read view中的属性进行可用性判断,如果不符合可见性的话,那就会通过DB_ROLL_PTR去undo log中取出上一个版本记录中的事务ID再进行可见性判断,以此类推,直到找到可见的记录为止。

可见性判断是使用可见性算法实现的,具体判断过程如下:

  1. 如果 DB_TRX_ID < up_limit_id 或 DB_TRX_ID == creator_trx_id,则该记录可见;
  2. 如果 DB_TRX_ID >= low_limit_id,则该记录不可见;
  3. 判断DB_TRX_ID是否在trx_list中;如果在,说明生成read view的时候,修改这个记录的事务还没提交,所以该记录不可见;如果不在trx_list中,说明生成read view之前修改这个记录的事务已经提交了,所以该记录可见。

事务隔离级别与MVCC的关系

READ UNCOMMITTED(读未提交)

READ UNCOMMITTED级别直接读取数据的最新记录,因此不会使用MVCC。

READ COMMITTED(读已提交)

READ COMMITTED级别会使用MVCC,在每次进行快照读都会生成新的read view,所以每次读取到的都是已提交的最新版本的记录,这样就可以解决脏读问题。

REPEATABLE READ(可重复读)

REPEATABLE READ级别会使用MVCC,只有在第一次进行快照读会生成read view,之后的快照读都会沿用第一次生成的read view,所以每次快照读读到的数据都是一样的,这样就可以解决脏读问题以及快照读的不可重复读、幻读问题。
RR级别下当前读的不可重复读和幻读问题
在快照读和当前读同时使用时,仍然有可能发生不可重复读和幻读的问题,需要使用next-key lock或行锁来解决。详细内容可以查看另一篇文章:MySQL RR级别下仍然会发生幻读和不可重复读

SERIALIZABLE(串行化)

SERIALIZABLE级别是通过加锁来访问数据,因此不会使用MVCC。

你可能感兴趣的:(MySQL,mysql,mvcc,可见性算法,幻读,不可重复读)