MVCC多版本并发控制

120 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情


1.概念

MVCC(Multiversion Concurrency Control),多版本并发控制,通过管理数据行的多个版本来实现数据库的并发控制。MVCC使得在事务的隔离级别下可以实现一致性读。

2.快照读与当前读

2.1.快照读

又叫一致性读,就是不加锁的非阻塞读,不加锁的简单的SELECT都属于快照读,它能提高并发性能,避免加锁,降低了开销。

快照读是基于多版本的,读取到的不一定是数据的最新版本;

快照读要求隔离级别不是串行级别,否则会退化为当前读。

2.2.当前读

读取记录的最新版本,同时保证其他并发事务不能修改当前记录,对读取的记录加锁。加锁的SELECT或者对数据进行增删改进行当前读。

3.隔离级别

在MySQL中,默认的隔离级别是可重复读,从定义上看不能解决幻读,需要上升到串行化,但是会大幅降低事务并发能力。

采用MVCC可以不采用锁机制,通过乐观锁的方式解决不可重复读和幻读,可以在大多数情况下代替行级锁,降低系统开销。

4.隐藏字段与版本链

4.1.隐藏字段

  • trx_id:每次一个事务对行数据进行改动时,把该事务的事务id赋给这条记录的trx_id字段
  • roll_point:每次事务对行数据进行改动时,会把旧的版本写入UNDO日志中,roll_point指针指向该日志,用来找到之前的版本信息

4.2.版本链

每次对记录的改动都会生成一条UNDO日志,每条日志也有也有roll_point属性,这些UNDO日志连起来构成了版本链,头结点是最新的版本。

5.ReadView

5.1.概念

ReadView就是事务在使用MVCC机制进行快照读操作时产生的读视图。

当事务启动时,会生成当前数据库系统的一个快照,InnoDB为每个事务构造了一个数组,记录了当前活跃事务的ID。

在MVCC机制中,多个事务对同一个行记录进行修改时会产生多个历史快照,都保存在UNDO LOG里。如果一个事务查询这条行记录,就需要用ReadView来判断读取哪一个版本的行记录。

5.2.设计思路

READ_COMMITTEDREPEATBLE READ隔离条件下,事务只能读已经提交的事务修改过的记录,事务在未提交的情况下,ReadView需要判断版本链中的那条数据是当前事务可见的。

ReadView中4个重要的内容:

  • creator_trx_id:创建这个读试图的事务的ID(只读事务的ID为0)
  • trx_ids:当前活跃的读写事务的ID列表
  • up_limit_id:活跃的事务中最小ID
  • low_limit_id:下一个事务应该分配的ID

要区分活跃事务与已提交事务

5.3.规则

如果被访问的版本的trx_id属性值:

  • 等于ReadView中的creator_trx_id,说明当前事务在访问它自己修改的记录,可以访问

  • 小于ReadView中的up_limit_id,说明生成该版本的事务已经提交,可以访问

  • 大于或等于ReadView中的low_limit_id,说明生成该版本的事务在当前事务之后才启动,不能访问

  • up_limit_idlow_limit_id之间,则判断是否在trx_ids中:

    • 若在,则说明仍在活跃的事务,不可访问
    • 若不在,则说明事务已提交,可以访问

5.4.流程

当要获取一条记录时:

  1. 获取事务ID
  2. 获取ReadView
  3. 查询得到的数据,与ReadView中的事务版本号进行比对
  4. 如果不可访问就获取历史快照
  5. 返回可以访问的数据

如果所有版本都不可访问,则查询结果不包括该记录。

  • READ COMMITTED中:

事务每进行一次查询操作都会重新获取一次ReadView,可能产生不可重复读和幻读的情况

  • REPEATABLE READ中:

事务只在第一次查询时获取一次ReadView,后面都复用这个读试图,所以避免了不可重复读(所以同时也解决了幻读)

5.5.实例

1.READ COMMITTED

  • 现有活跃事务10、20,新事务30读操作:

ReadView:

creator_trx_id:30 ;trx_ids:{ 10,20 };up_limit_id:10;low_limit_id:21

SELECT * FROM TABLE WHERE NUMBER = 1;

读出记录:‘张三’

  • 提交事务10,事务20更新记录,新事务30读操作:

ReadView:

creator_trx_id:30 ;trx_ids:{ 20 };up_limit_id:20;low_limit_id:21

读出记录:‘王五’

2.REPEATABLE READ

  • 现有活跃事务10、20,新事务30读操作:

ReadView:

creator_trx_id:30 ;trx_ids:{ 10,20 };up_limit_id:10;low_limit_id:21

读出记录:‘张三’

  • 提交事务10,事务20更新记录,新事务30读操作:

ReadView:

creator_trx_id:30 ;trx_ids:{ 10,20 };up_limit_id:10;low_limit_id:21

读出记录:‘张三’