什么是MVCC?MVCC(Multiversion Concurrency Control),多版本并发控制。顾名思义,MVCC是通过数据行的多个版本管理来实现数据库的并发控制。这项技术使得在InnoDB的事务隔离级别下执行一致性操作有了保证。换言之,就是为了查询一些正在被另一个事务更新的行,并且可以看到它们被更新之前的值,这样在做查询的时候就不用等待另一个事务释放锁。
MVCC的实现原理依赖于:隐藏字段、undo log 、Read View。多版本通过Undo log体现,并发控制通过Read View体现。
MVCC没有正式的标准,在不同的DBMS中MVCC的实现方式可能是不同的,也不是普遍使用的。本文学习InnoDB中MVCC的实现机制(MySQL其他的存储引擎并不支持它)。
【1】快照度和当前度
MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写
冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读,而这个读指的就是快照读,而非当前读。当前读实际上是一种加锁的操作,是悲观锁的实现。而MVCC本质是采用乐观锁思想的一种方式。
① 快照读
快照读又叫一致性读
,读取的是快照数据。不加锁的简单的select都属于快照读,即不加锁的非阻塞读。比如下例:
select * from player where ...
之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于MVCC,它在很多情况下,避免了加锁操作,降低了开销。
既然是基于多版本,那么快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。
快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读。
② 当前读
当前读读取的是记录的最新版本(最新数据,而不是历史版本的数据),读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。加锁的select,或者对数据进行增删改都会进行当前读。
示例:
select * from student lock in share mode; # 共享锁
select * from student for update ;#排他锁
insert into student values ... #排他锁
delete from student where ... #排他锁
update student set ... #排他锁
【2】MVCC
① 回顾隔离级别
我们知道事务有4个隔离级别,可能存在三种并发问题:
在MySQL中,默认的隔离级别是可重复读,可以解决脏读和不可重复读的问题。如果仅从定义的角度来看,它并不能解决幻读问题。如果我们想要解决幻读问题,就需要采用串行化的方式,也就是将隔离级别提升到最高,但这样一来就会大幅度降低数据库的事务并发能力。
MVCC可以不采用锁机制,而是通过乐观锁的方式来解决不可重复读和幻读问题。它可以在大多数情况下替代行锁,降低系统的开销。
② 隐藏字段和Undo Log版本链
回顾一下undo日志的版本链,对于使用InnoDB存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列。
- trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务id赋值给trx_id 隐藏列。
- roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。