定义
mvcc(Multiversion Concurrency Control)多版本并发控制,是现代数据库(包括Mysql、Oracle、PostgreSQL等)引擎实现中常用的处理读写冲突的手段,目的在于提高数据库并发场景下的吞吐性能。
- 说白了,mvcc就是“维持一个数据的多个版本“。
- 在Mysql中,mvcc只在读取已提交(Read committed) 和 重复读(Repeatable Read)两个事务级别下有效(Read uncommitted隔离级别下,读写都不加锁,Searalizabel隔离级别下,读写都加锁,也就不需要mvcc了)。
- 其是通过undo日志中的版本链和ReadView一致性视图来实现的。mvcc就是在多个事务同时存在时,select语句找寻到具体是版本链上的哪个版本,然后再找到的版本上返回其中所记录的数据的过程。
作用
mvcc在mysql innodb中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。
什么是当前读 和 快照读
在学习mvcc多版本并发控制之前,必须先了解一下,什么是mysql innodb下的当前读和快照读。
当前读
定义:读取到的记录是最新版本,读取时还要保证其他并发事务不能修改当前记录,灰度读取的记录进行加锁。
像select lock in share mode(共享锁),select for update,update ,insert , delete 这些操作都是一种当前读。
快照读
定义:
像不加锁的select操作就是快照读,即不加锁的非阻塞读。
快照读的前提是隔离级别不是串行,串行级别下的快照读会退化为当前读。
之所以出现快照读的情况是,是基于提高并发性能的考虑,快照读的实现是基于多版本控制,即mvcc,可以认为mvcc是行锁的一个变种,但他在很多情况下,避免了加锁操作,降低了开销。
实现原理
mvcc是基于undo log,隐藏字段,ReadView(读视图)实现的。
undo log(回滚日志)
undo log记录的是逻辑日志,也就是Sql语句。
比如:当我们执行一条insert语句时,undo log就记录一条相反的delete语句。
作用
- 回滚事务时,回到修改前的数据。
- 实现mvcc。
隐藏字段
当我们创建一张表时,innodb引擎会增加2哥隐藏字段。
- DB_TRX_ID(最近一次提交事务的id)
修改表数据时,都会提交事务,每个事务都有一个唯一的id,这个字段就记录了最近一次提交事务的id。 - DB_ROLL_PTR(上个版本的地址)
修改数据时,旧版本的数据都会被记录到undo log日志中,每个版本的数据都有一个版本地址,这个字段记录的就是上个版本的地址。
表记录和undo log历史数据组成了一个版本链
Read View 读视图
在事务中,执行sql查询,就会生成一个读视图,是用来保证数据的可见性,即读到undo log 种哪个版本的数据。
- 快照读一般是读取的是历史版本的读视图。
- 当前读会生成一个最新版本的读视图。
读视图是基于下面几个字段实现的:
- m_ids: 当前系统中活跃的事务ID集合,即未提交的事务。
- min_trx_id: m_ids种最小的id
- max_trx_id: 下一个要分配的事务ID。
- creator_trx_id: 当前事务ID
读视图决定当前事务能读到哪个版本的数据,从表记录到undo log历史数据的版本链,依次匹配,满足哪个版本的匹配规则,就能读取到哪个版本的数据,一旦匹配成功就不在往下匹配。
数据可见性规则
- DB_TRX_ID = creator_trx_id 如果这个版本数据的事务ID等于当前事务ID,表示数据记录的最后一次操作的事务就是当前事务,当前读视图可以读到这个版本的数据。
- DB_TRX_ID < min_trx_id 如果这个版本数据的事务ID小于所有活跃事务ID,表示这个版本的数据不再被事务使用,即事务已提交,当前读视图可以读到这个版本的数据。
- DB_TRX_ID >= max_trx_id 如果这个版本数据的事务ID大于等于下一个要分配的事务ID,表示有新事务更新了这个版本的数据,这种情况下,当前读视图不可以读到这个版本的数据。
- min_trx_id <= DB_TRX_ID < max_trx_id 如果这个版本数据的事务ID在当前系统中活跃的事务ID集合(m_ids)里面,表示这个版本的数据被其他事务更新过,当前读视图不可以读到这个版本的数据。 如果这个版本数据的事务ID不在当前系统中活跃的事务ID集合(m_ids)里面,表示是在其他事务提交后创建的读视图,当前读视图可以读到这个版本的数据。
不同隔离级别下可见性分析
在不同的事务隔离级别下,生成读视图的规则不同:
- READ COMMITTED(读已提交) :在事务中每一次执行快照读时都生成一个读视图,每个读视图中四个字段的值都是不同的。
- REPEATABLE READ(可重复读):仅在事务中第一次执行快照读时生成读视图,后续复用这个读视图。