MYSQL的MVCC机制浅析

MYSQL的MVCC机

什么是MVCC

MVCC,multi-version concurrency control,也就是多版本并发控制。是为了在读取数据时不加锁来提高读取效率和并发性的一种手段。

数据库并发有以下几种场景:

  • 读-读:不存在任何问题。
  • 读-写:有线程安全问题,可能出现脏读、幻读、不可重复读。
  • 写-写:有线程安全问题,可能存在更新丢失等。
    MVCC解决的就是读写时的线程安全问题,线程不用去争抢读写锁,提高读写性能

MVCC的实现

在实际应用中,数据库中的数据是要被多个用户共同访问的,在多个用户同时操作相同的数据时,可能就会出现一些事务的并发问题,具体如下:

  • 脏读:指一个事务读取到另一个事务未提交的数据。
  • 不可重复读:指一个事务对同一行数据重复读取两次,但得到的结果不同
  • 虚读/幻读:指一个事务执行两次查询,但第二次查询的结果包含了第一次查询中未出现的数据。
  • 丢失更新:指两个事务同时更新一行数据,后提交(或撤销)的事务将之前事务提交的数据覆盖了。

丢失更新可分为两类,分别是第一类丢失更新和第二类丢失更新。

  • 第一类丢失更新:是指两个事务同时操作同一个数据时,当第一个事务撤销时,把已经提交的第二个事务的更新数据覆盖了,第二个事务就造成了数据丢失。
  • 第二类丢失更新:是指当两个事务同时操作同一个数据时,第一个事务将修改结果成功提交后,对第二个事务已经提交的修改结果进行了覆盖,对第二个事务造成了数据丢失。

为了避免上述事务并发问题的出现,在标准的 SQL 规范中定义了四种事务隔离级别,不同的隔离级别对事务的处理有所不同。

四大隔离级别

  • 读未提交,Read Uncommitted
    • 这个很坑爹,就是说某个事务还没提交的时候,修改的数据,就让别的事务给读到了,这就恶心了,很容易导致出错的。这个也叫做脏读。
  • 读已提交,Read Committed(不可重复读)
    • 这个比上面那个稍微好一点,但是一样比较尴尬,就是说事务A在跑的时候, 先查询了一个数据是值1,然后过了段时间,事务B把那个数据给修改了一下还提交了,此时事务A再次查询这个数据就成了值2了,这是读了人家事务提交的数据啊,所以是读已提交。这个也叫做不可重复读,就是所谓的一个事务内对一个数据两次读,可能会读到不一样的值。
  • 可重复读,Read Repeatable
    • 这个就是比上面那个再好点儿,就是说事务A在执行过程中,对某个数据的值,无论读多少次都是值1;哪怕这个过程中事务B修改了数据的值还提交了,但是事务A读到的还是自己事务开始时这个数据的值【MYSQL默认的隔离级别】。
  • 串行化
    • 幻读,不可重复读和可重复读都是针对两个事务同时对某条数据在修改,但是幻读针对的是插入,比如某个事务把所有行的某个字段都修改为了2,结果另外一个事务插入了一条数据,那个字段的值是1,然后就尴尬了。第一个事务会突然发现多出来一条数据,那个数据的字段是1。如果要解决幻读,就需要使用串行化级别的隔离级别,所有事务都串行起来,不允许多个事务并行操作。

其中可重复读和读已提交是基于MVCC实现快照读

具体实现

MYSQL底层针对每一条记录都会存在两个隐藏字段

  • trx_id:用于存储每次对这个记录进行操作的事务ID
  • roll_pointer:每次对这个记录进行修改的时候,会将老版本记录写入undo日志,而这个字段就是为了存储一个指针,指向老版本的位置,通过它来获取老版本的信息,这样就组成了一个版本链
  • read view:读视图,用于判断哪些版本对当前事务可见,是由未提交的事务ID数组和其最小值和最大值组成。

读视图判断一个事务对当前是否可见的规则是:不在未提交的事务ID数组中并且当前事务ID<数组最大值或者是自己

那么MVCC主要是通过读视图和当前事务的ID,根据读视图可见的规则,来返回可见的具体数据来实现。

具体案例来看
在这里插入图片描述
比如当前我们的数据库隔离级别是可重复读,上面我们一条记录现在同时有四个事务在操作,但是只有事务100已提交,其他事务都未提交。这时候我们新增一个事务120.。那么当前读视图中的数据为:105 [105,108,120,200] 200

  • 最小事务:105
  • 最大事务:200
  • 未提交事务ID数组:[105,108,120,200]

根据读视图可见性原理,当前可见事务需要不在未提交事务数组内,并且当前事务ID<数组最大值或者是自己。那么可见的事务ID就只有100,那么查询到的数据就是张三

如果变更事务隔离级别为已提交读,并且在之后事务108进行了事务提交,那么这时候查询到的数据是什么?

  • 那么这时候查询到的数据就会变成王二
  • 如果事务隔离级别是可重复读的话,拿还是读取到的是张三
  • 这是因为当是已提交读的话,会在查询的时候重新去拿readView,所以这时候读视图数据就变成了:105 [105,120,200] 200,所以最新可见的事务ID变成了108.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值