在MySQL中,MVCC是一种用于提供并发控制的技术,它允许数据库系统在事务并发执行的情况下保持数据的一致性,同时提高了数据库的并发性能。MVCC背后的理念是允许每个事务可以看到一个一致性的快照,从而避免了读取操作被写入操作所阻塞的情况。不用加锁,解决多并发场景下的快照读问题
1 MVCC的原理
MVCC的核心思想是为每个数据行保存其在不同时间点的版本,并通过版本号或时间戳来标识这些版本。当一个事务开始时,MySQL会为该事务创建一个独立的事务ID,并利用这个ID去获取数据的一致性快照。
Undo日志
:在MySQL中,MVCC的实现依赖于undo日志(也称为回滚日志)。当某个事务对数据进行修改时,MySQL不会立即覆盖原始数据,而是将修改前的原始数据保存到undo日志中。这样,其他事务仍然可以访问到这个数据的旧版本,从而实现了数据的多版本管理。ReadView
:每个事务在启动时都会创建一个ReadView,这个视图通过检查undo日志中的信息和事务的时间戳来确定哪些数据版本对于当前事务是可见的。这样,对于已提交的数据版本,ReadView会返回最新的版本,对于未提交的数据版本,ReadView则会返回旧的版本或者忽略这些数据。
2 当前读和快照读
2.1 当前读
查询当前已提交最新的数据
- SELECT LOCK IN SHARE MODE;(共享锁)
- SELECT FOR UPDATE;(排它锁)
- UPDATE(排它锁)
- DELETE(排它锁)
- INSERT(排它锁)
- 串行化事务隔离级别
2.2 快照读
普通的select查询,通过MVCC进行版本链查找就叫快照读
3 版本链
3.1 什么是版本链
首先聚簇索引的叶子节点存储数据包括两个隐藏字段:trx_id(事务ID)、roll_pointer(回滚指针)
- trx_id(事务ID):记录的是修改当前这行数据的事务ID
- roll_pointer(回滚指针):记录该行数据上个版本的地址
因此版本链其实就是由回滚指针(roll_pointer)串起来的该行数据对应的各个版本。
3.2 read view
MySQL中的Read View(读视图)是多版本并发控制(MVCC)机制的一部分。在多用户并发访问数据库时,read view 能够确保每个用户看到的数据都是一致的,避免了脏读、不可重复读等问题。
read view中四个核心参数:
- m_ids:当前活跃事务ID列表
- min_trx_id:活跃事务最小ID
- max_trx_id:下一个事务ID
- creator_trx_id:当前事务ID
如何判断版本链中可用版本:
4 MVCC在读已提交和可重复读下区别
根本原因在于RC隔离级别下事务中每次select都会创建一个read view,而RR隔离级别只会在事务中创建一次read view
4.1 读已提交场景
简单理解:读取已提交最新的数据,能解决脏读,有不可重复度问题。
根本原因:读已提交事务隔离级别下,事务里面每次select查询都会创建一个read view。
举例说明:事务A 进行一次修改,事务B进行两次查询
- 首先开启A事务并修改为‘北冥-2’,事务未提交。(修改前查询最新数据时会生成read view)
- 然后开启事务B进行select查询,此时查询到的数据为‘北冥’,read view 如下
- 事务A提交事务。数据被提交到磁盘。
- 事务B再次相同select查询,read view如下,此时min_trx_id变成了2,当判断trx_id < min_trx_id满足条件,所以获取到最新数据,和之前数据不一致。因此不可重复度。
4.2 可重复读场景
简单理解:读取已提交最新的数据,能解决脏读,有不可重复度问题。
根本原因:可重复读事务隔离级别下,每个事务只会创建一个read view,同一个事务里面的select操作共用同一个read view。
举例说明:事务A 进行一次修改,事务B进行两次查询
- 首先开启A事务并修改为‘北冥-2’,事务未提交。(修改前查询最新数据时会生成read view)
- 然后开启事务B进行select查询,此时生成read view,根据版本链查询到的数据为‘北冥’
3. 即便后面事务A已经提交了事务,最新数据被更改为了‘北冥-2’,但是事务B的read view 还是之前那个,在根据上面条件进行判断时最新数据中trx_id=2不符合,根据回滚指针找到下一条数据trx_id=1才符合,因此读到的数据跟第一次是一样的。
mysql解决了幻读吗?
只解决了快照读下的幻读,而当前读需要通过间隙锁
RC隔离级别下有间隙锁吗?
没有,
参考
B站 IT老哥 :阿里P7要求这么低吗?老哥给你讲清楚什么是MySQL的MVCC