说一下MVCC
15 分钟阅读
•
1686 字
+
1409 词
mysql利用MVCC无锁机制。
==
避免读写阻塞。
==
单纯使用锁的时候,并发性能会比较差。即便是在读写锁这种机制下,读和写依旧是互斥的。而数据库是一个
性能非常关键的中间件
,如果某个线程修改某条数据就让其他线程都不能读这条数据,这种性能损耗是无法接受的。
所以 InnoDB 引擎引入了 MVCC,就是为了
减少读写阻塞。
MVCC是什么
MVCC 是 MySQL InnoDB 引擎用于控制数据并发访问的协议。MVCC 主要是借助于
版本链
来实现的。在 InnoDB 引擎里面,每一行都有两个额外的列,一个是
trx_id
,代表的是修改这一行数据的事务 ID。另外一个是
roll_ptr
,代表的是回滚指针。InnoDB 引擎通过回滚指针,将数据的不同版本串联在一起,也就是版本链。这些串联起来的历史版本,被放到了 undolog 里面。当某一个事务发起查询的时候,MVCC 会根据
事务的隔离级别
来生成不同的 Read View,从而控制事务查询最终得到的结果。
这种通过「版本链」来控制并发事务访问同一个记录时的行为就叫 MVCC(多版本并发控制)。结合read_view来进行CAS比较,所以说这是一种乐观锁的表现
数据库表中除了原数据的这些列以外,还维护了3个我们看不到的隐藏列
DB_
TRX_ID
当前
这条数据被哪个事务维护了
DB_
ROLL_PTR
存储这
条新数据的老数据的指针
,一旦需要更新的操作需要回滚,就会利用这个指针来进行数据回滚,这些历史数据存在了undolog里面,以链表的方式进行存储。
回滚指针
。InnoDB 通过 roll_ptr 把
每一行的历史版本串联在一起
。
在没有维护主键ID的时候,会维护一个
ROW_ID
的隐藏列。利用这个来建主键索引
创建一个readview快照信息,再通过这个readview去找undolog里的每一条信息。按规则一个个找。
MVCC就是乐观锁的一种体现
读未提交
无需锁无需MVCC,因为修改数据改源数据,会出现脏读
读已提交
每次查询都会创建Readview读取数据
可重复读
同样的查询只会第一次创建Readview快照
(每个事务对应一个readview)
串行化
直接使用的表锁,或者是读写锁
对于「读提交」和「可重复读」隔离级别的事务来说,它们是通过 Read View 来实现的,它们的区别
在于创建 Read View 的时机不同
,大家可以把 Read View 理解成一个数据快照,就像相机拍照那样,定格某一时刻的风景。
「读提交」隔离级别是在「每个语句执行前」都会重新生成一个 Read View,
而「可重复读」隔离级别是「启动事务时」生成一个 Read View,然后整个事务期间都在用这个 Read View
Readview有四个重要的字段
在创建readview后,可以将记录中的trx_id划分为这三种情况
这样,对于当前事务的启动瞬间来说,一个数据版本的 row trx_id,有以下几种可能:
为什么要用MVCC?
MVCC是事务隔离级别的无锁的一种实现方式
- 如果落在 绿色部分 ,表示这个版本是已提交的事务或者是当前事务自己生成的,这个 数据是可见 的;
- 如果落在 红色部分 ,表示这个版本是由将来启动的事务生成的,是 肯定不可见 的;
-
如果落在
黄色部分
,那就包括两种情况
- a. 若 row trx_id 在m_ids数组中,表示这个版本是由 还没提交 的事务生成的,不可见;
- b. 若 row trx_id 不在m_ids数组中,表示这个版本是 已经提交了 的事务生成的,可见。
然而,MVCC 并不能完全替代锁的使用,原因如下:
1.
数据一致性
- 强一致性要求 :在某些情况下,应用程序需要确保数据的一致性,如在金融交易中,多个操作必须以原子方式完成。仅使用 MVCC 可能无法保证在并发环境下的强一致性。
- 防止脏读和不可重复读 :虽然 MVCC 可以帮助避免脏读,但在某些隔离级别下(如可重复读),仍可能需要锁来防止不可重复读和幻读。
2.
复杂的事务管理
- 长事务 :当一个事务持有长时间的锁时,MVCC 可能无法有效管理版本。长事务可能导致数据版本太多,增加内存和存储的负担。
- 事务的回滚 :在发生错误或需要回滚时,MVCC 无法处理某些复杂的场景,比如在多个事务之间的依赖关系,需要锁来提供清晰的事务边界。
3.
高竞争情况下的性能问题
- 写冲突 :在高竞争的情况下,如果多个事务尝试同时更新同一行数据,仅依赖 MVCC 可能导致大量的版本产生,进而影响性能。使用锁可以有效控制对同一资源的访问,从而减少写冲突的发生。
- 锁的优先级 :在一些场景中,开发者可能希望通过锁来优先处理某些重要的事务,而 MVCC 无法提供这样的控制。
4.
特定的应用场景
- DDL 操作 :在执行数据定义语言(DDL)操作时,通常需要加锁以防止其他事务同时修改结构,这在 MVCC 中是无法处理的。
- 复杂查询 :某些复杂的查询可能需要在读取数据的一致性上依赖于锁,以确保在查询过程中数据不会被修改。
5.
实现复杂性
- 实现复杂度 :完全依赖 MVCC 可能会导致实现复杂性增加,尤其是在处理死锁、回滚和锁竞争等问题时。锁机制提供了相对简单而有效的方式来管理并发。