多版本并发控制MVVC,Multi-Version Concurrency Control,通过数据行的多个版本来控制数据库的并发。mysql只有InnoDB引擎才支持MVVC.
通过管理每条记录的多个版本,实现数据库事务并发时一致性读,当前事务A读取正在被其他事务B更新的数据行时,事务A能读到被事务B更新之前的记录。解决读写冲突问题。
要明白MVVC的原理,需要知道三个知识点:隐式字段、Undo Log、Read View
1、隐式字段
在mysql数据库中的,每条记录,除了我们自己定义的字段外,还有数据库定义的几个隐式字段
字段名称 | 字段 | 占用 | 作用 |
---|---|---|---|
隐式主键 | DB_ROW_ID | 6 byte | 如果表没有创建主键,InnoDB引擎会根据DB_ROW_ID 产生一个索引 |
事务ID | DB_TRX_ID | 6 byte | 最新的事务ID |
回滚指针 | DB_ROLL_PTR | 7 byte | 指向这条记录的上一个版本 |
2、回滚日志 Undo Log
当一个事务进行写操作时,mysql将需要修改的数据的旧版本数据,写到Undo Log中,然后再将新的数据写入到数据库中。在事务提交之前,Undo Log中的数据可用于回滚。
Undo Log的版本链,是一个链表,用于维护这些历史数据版本。在这个版本链中,每个版本对应一个版本号或时间戳,新版的数据会添加到这个链表的头部。
回滚指针把该数据行的所有版本连接起来。
多个事务对同一条记录进行操作,会产生多个版本,但如果一个事务想查询某行记录,那这个事务需要读取哪个历史版本的数据哪?哪些历史版本的数据,该事务允许被读取到?这就会用到下一个概念:Read View
3、Read View
Read View 是事务进行快照读操作时产生的读视图。在事务进行快照读的那一时刻,会产生一个数据库当前的快照,记录并维护系统当前活跃的事务id,并且该事务id值是递增的。
Read View的几个全局变量:
- creator_trx_id,创建读视图的事务id,增、删、改事务该有资格分配该事务id,读事务的id为0
- trx_ids, 表示在生成该读视图时,当前系统活跃的读写事务的事务id列表
- up_limit_id, 活跃事务列表中,最小的事务id,也就是最早的事务id
- low_limit_id, 在生成Read View时,数据库系统应该分配的下一个事务id,也就是活跃列表中的最大id。
Read View 最大的作用就是用来做可见性判断的。用每个事务当时产生的读快照视图,来判断该事务能够看到哪个版本的数据.
可见性算法小结:
一个Read View版本,对于该事务来说,数据总是可见的。其他情况
- 该事务id< up_limit_Id,也就是活跃列表中最小的事务id时,该版本对该事务可见。
- 事务id>=low_limit_id,该版本对于该事务是不可见的。
- 事务id在up_limit_id 和low_limit_id之间,需要判断是否属于活跃列表,如果属于活跃列表,则不可见,否则可见。
4、具体应用
READ UNCOMMITTED隔离级别:读未提交,直接返回最新版本的数据即可
READ COMMITTED和REPEATABLE READ,这两个隔离级别,都必须保证读到已提交的数据。核心问题就是,需要判断版本列表中哪个版本是当前事务可见的,这就是Read View要解决的重要问题。
SERIABLIZABLE隔离级别,InnoDB是使用加锁的方式来实现。