MVCC(多版本并发控制)
-
MVCC叫做多版本并发控制,读取数据时通过一种类似快照的方式将数据保存下来,不同的事务会看到自己特定版本的数据,这样读锁和写锁就不冲突了,提高了数据库的读写效率。
-
MVCC主要是处理读请求的,这个读主要处理的是快照读,而不是当前读。
- 快照读(一致性非锁定读),是基于MVCC的,读到的数据不一定是当前最新的数据,有可能是之前历史版本的数据。
- 当前读(锁定读),其实是一种悲观锁,需要去加锁。读取的是最新版本的数据,比如我们执行insert、update或delete的时候,都需要先读取数据,再去操作。
-
事务的
ACID特性中的隔离性,除了通过锁来保证,也是通过 MVCC保证的。而且 MVCC是只在READ COMMITTED(读已提交)和REPEATABLE READ(可重复读)两个隔离级别下工作。其他两个隔离级别都和 MVCC不兼容,因为READ-UNCOMMITTED(读取未提交) 总是读取新的数据,而SERIALIZABLE(可串行化)会对所有读取的行都加锁。 -
MVCC实现的主要流程:在
InnoDB存储引擎中,创建一个新事务后,执行每个select语句前,都会创建一个快照Read View,ReadView中主要保存了 :m_creator_trx_id(创建该ReadView的事务ID)m_ids(活跃事务id列表:一个当前对本事务不可见的其他活跃事务id的列表)m_low_limit_id(当前ReadView出现过的最大的事务 ID+1,即下一个将被分配的事务 ID。大于等于这个 ID 的数据版本均不可见)m_up_limit_id(活跃事务列表m_ids中最小的事务 ID,如果m_ids为空,则m_up_limit_id为m_low_limit_id。小于这个 ID 的数据版本均可见)
当用户在这个事务中要读取某个记录行时,
InnoDB会将该记录行的隐藏字段DB_TRX_ID(最后一次插入或更新该行的事务 id)与ReadView中的一些变量及当前事务ID进行比较,判断是否满足可见性条件,规则如下:- 如果记录 DB_TRX_ID < m_up_limit_id,那么表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照之前就提交了,所以该记录行的值对当前事务是可见的。
- 如果 DB_TRX_ID >= m_low_limit_id,那么表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照之后才修改该行,所以该记录行的值对当前事务不可见。跳到步骤 5。
- m_ids 为空,则表明在当前事务创建快照之前,修改该行的事务就已经提交了,所以该记录行的值对当前事务是可见的。
- 如果 m_up_limit_id <= DB_TRX_ID < m_low_limit_id,表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照的时候可能处于“活动状态”或者“已提交状态”;所以就要对活跃事务列表 m_ids 进行查找(源码中是用的二分查找,因为是有序的)。
- 如果在活跃事务列表 m_ids 中能找到 DB_TRX_ID,表明:① 在当前事务创建快照前,该记录行的值被事务 ID 为 DB_TRX_ID 的事务修改了,但没有提交;或者 ② 在当前事务创建快照后,该记录行的值被事务 ID 为 DB_TRX_ID 的事务修改了。这些情况下,这个记录行的值对当前事务都是不可见的。跳到步骤 5。
- 在活跃事务列表中找不到,则表明“id 为 trx_id 的事务”在修改“该记录行的值”后,在“当前事务”创建快照前就已经提交了,所以记录行对当前事务可见。
- 在该记录行的隐藏字段
DB_ROLL_PTR(回滚指针,指向该行的undo log。如果该行未被更新,则为空)指针所指向的undo log取出快照记录,用快照记录的 DB_TRX_ID 跳到步骤 1 重新开始判断,直到找到满足的快照版本或返回空。
-
通过ReadView生成策略的不同,从而实现不同的隔离级别。
- 读已提交隔离级别下的事务在每次查询的开始都会生成一个独立的
ReadView。 - 而可重复读隔离级别下的事务在第一次查询的时候生成一个
ReadView,之后的读都复用之前的ReadView。
- 读已提交隔离级别下的事务在每次查询的开始都会生成一个独立的
-
补充:
InnoDB存储引擎为每行数据记录添加了三个隐藏字段:DB_TRX_ID(6字节):表示最后一次插入或更新该行的事务 id。此外,delete操作在内部被视为更新,只不过会在记录头Record header中的deleted_flag字段将其标记为已删除。DB_ROLL_PTR(7字节)回滚指针,指向该行的undo log。如果该行未被更新,则为空。DB_ROW_ID(6字节):如果没有设置主键且该表没有唯一非空索引时,InnoDB会使用该 id 来生成聚簇索引。
undo-log:
undo log主要有两个作用:- 当事务回滚时用于将数据恢复到修改前的样子
- 另一个作用是
MVCC,当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过undo log读取之前的版本数据,以此实现非锁定读
在
InnoDB存储引擎中undo log分为两种:insert undo log和update undo log:-
insert undo log:指在insert操作中产生的undo log。因为insert操作的记录只对事务本身可见,对其他事务不可见,故该undo log可以在事务提交后直接删除。不需要进行purge操作 -
update undo log:update或delete操作中产生的undo log。该undo log可能需要提供MVCC机制,因此不能在事务提交时就进行删除。提交时放入undo log链表,等待purge线程进行最后的删除。
不同事务或者相同事务的对同一记录行的修改,会使该记录行的undo log成为一条链表,链首就是最新的记录,链尾就是最早的旧记录。
tip:以上为本人参考各类资料所整理的,若有大佬发现错误,请评论指出,感谢!
603

被折叠的 条评论
为什么被折叠?



