MySQL之MVCC

MVCC(多版本并发控制)

  • MVCC叫做多版本并发控制,读取数据时通过一种类似快照的方式将数据保存下来,不同的事务会看到自己特定版本的数据,这样读锁和写锁就不冲突了,提高了数据库的读写效率。

  • MVCC主要是处理读请求的,这个读主要处理的是快照读,而不是当前读。

    • 快照读(一致性非锁定读),是基于MVCC的,读到的数据不一定是当前最新的数据,有可能是之前历史版本的数据。
    • 当前读(锁定读),其实是一种悲观锁,需要去加锁。读取的是最新版本的数据,比如我们执行insert、update或delete的时候,都需要先读取数据,再去操作。
  • 事务的ACID特性中的隔离性,除了通过锁来保证,也是通过 MVCC保证的。而且 MVCC是只在 READ COMMITTED(读已提交)和 REPEATABLE READ(可重复读)两个隔离级别下工作。其他两个隔离级别都和 MVCC不兼容,因为READ-UNCOMMITTED(读取未提交) 总是读取新的数据,而 SERIALIZABLE(可串行化)会对所有读取的行都加锁。

  • MVCC实现的主要流程:在 InnoDB 存储引擎中,创建一个新事务后,执行每个 select 语句前,都会创建一个快照 Read ViewReadView 中主要保存了 :

    • 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_idm_low_limit_id。小于这个 ID 的数据版本均可见)

    当用户在这个事务中要读取某个记录行时,InnoDB会将该记录行的隐藏字段 DB_TRX_ID(最后一次插入或更新该行的事务 id)与ReadView中的一些变量及当前事务ID进行比较,判断是否满足可见性条件,规则如下:

    1. 如果记录 DB_TRX_ID < m_up_limit_id,那么表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照之前就提交了,所以该记录行的值对当前事务是可见的。
    2. 如果 DB_TRX_ID >= m_low_limit_id,那么表明最新修改该行的事务(DB_TRX_ID)在当前事务创建快照之后才修改该行,所以该记录行的值对当前事务不可见。跳到步骤 5。
    3. m_ids 为空,则表明在当前事务创建快照之前,修改该行的事务就已经提交了,所以该记录行的值对当前事务是可见的。
    4. 如果 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 的事务”在修改“该记录行的值”后,在“当前事务”创建快照前就已经提交了,所以记录行对当前事务可见。
    5. 在该记录行的隐藏字段 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 logupdate undo log

    • insert undo log :指在 insert 操作中产生的 undo log。因为 insert 操作的记录只对事务本身可见,对其他事务不可见,故该 undo log 可以在事务提交后直接删除。不需要进行 purge 操作

    • update undo logupdatedelete 操作中产生的 undo log。该 undo log可能需要提供 MVCC 机制,因此不能在事务提交时就进行删除。提交时放入 undo log 链表,等待 purge线程 进行最后的删除。
      不同事务或者相同事务的对同一记录行的修改,会使该记录行的 undo log 成为一条链表,链首就是最新的记录,链尾就是最早的旧记录。

tip:以上为本人参考各类资料所整理的,若有大佬发现错误,请评论指出,感谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值