MySQL MVCC实现原理

什么是MVCC:

MVCC 全名Multi Version Concurrency Control 多版本控制, 是指在数据库中为了实现高并发的数据访问,对数据进行多版本处理,并通过事务的可见性来保证事务能看到自己应该看到的数据版本。

MVCC 在MySQL Innodb中的实现主要是为了提高数据库的并发性,更好的处理读写冲突问题。

MVCC中的几个概念:

  • 当前读:读取的是最新版本的数据,且读取是其它并发事务不能修改当前记录,需要对读操作进行加锁。如: select lock in share mode (共享锁),  select for update; insert; delete; update (排它锁), 这些操作都属于当前读操作。
  • 快照读:不加锁的select 操作就属于快照度, 即不加锁的非阻塞读。 快照度的前提是隔离界别不是串行级别, 串行级别下的快照度会退化成当前读。(简单理解为,没有事务的select语句)

MVCC实现原理: 

MVCC的实现分为三个部分: 表中三个隐藏的字段、 undo log日志、 read view

隐藏的字段:

mysql中每行记录除了我们自定义的字段外,还有三个隐藏的字段:

  • DB_TRX_ID:记录这行数据最后一次事务ID
  • DB_ROLL_PTR:回滚指针,指向该行数据的上一个版本(存储在rollback segment中)
  • DB_ROW_ID :隐藏的自增ID, 如果表结构中没有主键,则它作为隐藏主键。

实际还有一个删除flag 隐藏字段, 如果数据被删除或更新,并非真的删除,只是改变删除flag。

 undo log:

之前介绍过undo log主要用来实现数据库回滚和mvcc。这里我们就看看它如何实现mvcc

undo log 主要分为两种:

  •  insert  undo log: 代表insert 类型事务操作时产生的undo log日志,只在事务回滚时需要,且在事务提交后立即抛弃
  • update undo log: update、delete操作时产生的undo log。在事务回滚和快照读时都需要,只有在快照读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除。

其中只有update undo log能配合完成mvcc操作。 

 下面我们看一下undo log 的记录过程:

  1. 我们对表中的一条原始数据做update 操作:这时该行记录的事务ID自增加1 ,undo log中的记录可以理解为下面这幅图
  2. 再次对该行记录做update操作,这时事务ID再次自增加1,undo log中的记录可以理解为下面这幅图 :

 从上面我们可以知道,不同或相同的事务对同一行数据的操作,在undo log中会生成一条记录版本的线性表。链表的头结点是最新版的数据记录,链尾是最早的数据记录。

Read View 

Read View 是事务进行快照读操作时产生的读视图(Read View), 主要是用来做可见性判断。

在一个事务执行快照读的时候,会生成数据库系统当前的一个快照, 记录并维护当前活跃事务的ID(当每个事务开启时,就会被分配一个递增的ID,所以最新的事务ID最大 )。

Read View 工作原理主要包含三个部分:

  1. trx_ids:包含当前正在执行的所有事务ID
  2. trx_min_id、trx_max_id:  trx_ids中最小的事务ID 和 read view生成时系统尚未分配的下一个事务ID(官方定义这两个变量为 up_limit_id(最小)、low_limit_id(最大),个人觉得这两个变量定义的很别扭)
  3. 比较事务的可见性:
    1. 如果数据的DB_TRX_ID < trx_min_id, 则当前事务能看到DB_TRX_ID的记录, 否则进入下一个判断
    2. 如果DB_TRX_ID >= trx_max_id,则该行记录是在Read View生成后才出现的, 那对当前事务肯定是不可见的, 否则进入下一个判断
    3. 如果 trx_min_id <= DB_TRX_ID < trx_max_id, 则在Read View生成时, 该事物A仍在活跃状态,事务A修改的数据,在当前事务B中是不可见的

事务隔离级别与MVCC:

从上面的介绍我们看到了MVCC是如何实现事务间的隔离的,而mysql事务隔离有四种级别:

隔离级别存在的问题
READ-UNCOMMITTED (RU)脏读、不可重复读、幻读
READ-COMMITTED      (RC)不可重复读、幻读
REPEATABLE-READ     (RR)幻读
SERIALIZABLE              (SE)

RU是不存在事务的隔离的,SE的锁级别完全屏蔽了数据库的并发效果。因此这里只需要讨论RC和RR两种级别在MVCC中的差别。

RR是RC的更高一级隔离级别,在RC的基础上解决了不可重复读的问题。我们来比较一下两者的快照读的区别:

1、事务开启后,先进行查询生成快照

表1: 事务B在事务A提交后,查询的数据依然是500,而当前数据已经更新成400

事务A事务B
开启事务开启事务
快照读(无影响)查询金额为500快照读查询金额为500
更新金额为400
提交事务
select 快照读金额为500
select lock in share mode当前读金额为400

2、事务开启后,先不进行查询,等另一事务提交后再生成快照

表2:

事务A事务B
开启事务开启事务
快照读(无影响)查询金额为500                   
更新金额为400
提交事务
select 快照读金额为400
select lock in share mode当前读金额为400

二者唯一的区别是,表1中先生成快照,后被事务A修改数据;表2先被事务A修改数据,后生成快照,二者数据的可见性完全不同。由此可以看出事务中快照读的结果与该事务首次出现快照读的时机有很大关系,它决定了事务后续快照读的结果。

因此RR与RC数据的隔离性与它们的快照生成时间有关。

  • RR下的事务对某条记录的第一次快照读会创建一个快照和Read View, 将当前系统活跃的其它事务记录下来,后面再调用快照读时,还是使用当前Read View。所以只要当前事务在其它事务提交更新之前使用过快照读,name之后的快照读使用的都是同一个Read VIew,trx_min_id <= DB_TRX_ID < trx_max_id ,所以对之后的修改不可见。
  • RR下,快照读生成Read View时, Read View会记录此时所有其它活动事务的快照,这些事务的修改对于当前事务都是不可见的,但早于Read View创建的事务所做的修改都是可见的
  • RC下, 事务中每次快照读都会新生成一个快照和Read View, 这就是我们在RC级别下的事务中科技看到别的事务提交的更新的原因。

总之在 RC 隔离级别下,是每个快照读都会生成并获取最新的 Read View;而在 RR 隔离级别下,则是同一个事务中的第一个快照读才会创建 Read View, 之后的快照读获取的都是同一个 Read View

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值