目录

【MySQL】版本并发控制MVCC

一致性视图

MySQL中,有两个视图的概念:

  • 一个是view,它是用查询语句定义的虚拟表,在调用时执行查询语句,并且生成结果。

  • 另一个是InnoDB在实现MVCC时用到的一致性读视图,用于支持RCRR隔离级别。

对隔离级别的影响

隔离级别是利用一致性视图来实现的:

  • 可重复读隔离级别下,视图是在事务启动时创建的,整个事务存在期间都用这个视图。

  • 读提交级别下,视图是在每个SQL语句开始执行时创建的。

  • 读未提交级别下,直接返回记录上的最新值,没有视图概念。

  • 串行化级别下,通过直接加锁的方式避免并行访问。

事务启动时机

begin/start transaction并不是一个事务的启动时间,只有执行到它们之后第一个操作InnoDB表的语句时,事务才启动。

对于可重复读隔离级别,一致性视图是在执行第一个快照读语句时创建。也可以使用start transaction with consistent snapshot启动一个事务,同时立即创建一个一致性视图。

如果autocommit=1,则每个语句就是一个事务,InnoDB会隐式执行begincommit

事务ID

InnoDB的每个事务有一个唯一的事务ID,叫transaction id。事务开始时申请,严格递增。

每行数据都有多个版本,每个版本有自己的row trx_id。事务更新数据时,会生成新的数据版本,并且把transaction id赋值给这个版本的row trx_id

视图数组

视图数组里保存的是,启动了但是还没提交的事务ID。

数组里事务ID最小值称为低水位,系统已经创建过的事务ID最大值称为高水位

视图数组和高水位,组成了当前事务的一致性视图(read-view)。数据版本的可见性,是基于数据的row trx_id和一致性视图对比得到。

InnoDB就是这样利用所有数据都有多个版本的特性,实现了秒级创建快照的能力。

数据可见性

对于某个数据版本的row trx_id

  • 如果等于当前事务的事务ID,当前事务可见。

  • 如果小于低水位,当前事务可见。

  • 如果大于高水位,当前事务不可见。

  • 如果不小于低水位不大于高水位,且不在视图数组中,当前事务可见。

  • 如果不小于低水位不大于高水位,且在视图数组中,不可见。

总结就是,对于一个事务视图:

  • 自己的更新总可见。

  • 版本未提交,不可见。

  • 版本已提交,但是是在视图创建后提交,不可见。

  • 版本已提交,而且是在视图创建前提交,可见。

MVCC

回滚日志

每条记录更新时,都会记录一条回滚日志。通过回滚日志,可以得到前一个状态的值。

MVCC

同一条记录在系统中可以存在多个版本,这就是版本并发控制(MVCC)。

不同时刻启动的事务有不同的一致性视图,一致性视图结合回滚日志实现数据的版本并发控制。

通过一致性视图判断当前版本数据是否可见,如果不可见,就使用回滚日志查找它的上一个版本。

当没有事务需要用到这些回滚日志时,即没有比这个回滚日志更早的视图,回滚日志会被清除。

当前读

当前读总是读取已经提交完成的最新版本,并且会对读取的记录加锁。

更新数据时都是先读后写的,而且只能读当前值,此时的读是当前读。

查询数据时,如果对select语句显式加锁,则也是当前读。加锁逻辑为:

  • lock in share mode加读锁(S锁,共享锁)。

  • for update加写锁(X锁,排他锁)。

当前读的实现

当前读是依靠next-key锁来实现的。

快照读

快照读总是读取记录的可见版本,不用加锁。如简单的select语句即为快照读。

快照读的实现

快照读依靠MVCCundo log来实现的。