MySQL 事务隔离级别和MVCC版本控制

一、事务并发执行的问题

事务并发的问题有四种分别是脏写、脏读、不可重复读、幻读。其中脏读、脏写是在事务并发中读取到了未提交事务修改过的数据,这会导致比较严重的问题。

问题描述
脏写一个事务修改了另一个未提交事务修改过的数据,那就意味着发生了脏写
脏读一个事务读到了另一个未提交事务修改过的数据,那就意味着发生了脏读
不可重复读一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,那就意味着发生了不可重复读
幻读一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来,那就意味着发生了幻读

二、MySQL支持的隔离级别

MySQL的默认隔离级别为REPEATABLE READ

  • READ UNCOMMITTED:未提交读,会导致脏读、不可重复读、幻读
  • READ COMMITTED:已提交读,会导致不可重复读、幻读
  • REPEATABLE READ:可重复读,会导致幻读
  • SERIALIZABLE:可串行化,可以杜绝并发出现的问题,但是效率较低。

三、MVCC 版本控制

1. 基础知识

MySQL设计实现版本控制,主要通过InnoDB page记录中的字段和Undo Log实现版本链记录,结合ReadView 事务id的比较实现版本控制。

  1. InnoDB聚族索引中存在两个必要的隐藏列
    • row_id 可以是主键,如果没有主键则是自动生成隐藏列。
    • trx_id 事务对聚族索引改动时将当前修改的事务id赋值给trx_id。
    • roll_pointer 对应undo log日志位置,可以回溯到历史版本。
  2. 版本链
    在多个事务对同一条记录修改时,将修改前的记录保存在Undo Log中,多次的修改将会生成一个版本链。其中tri_id就是不同事务修改赋值给聚族索引的事务id。
    在这里插入图片描述
    在这里插入图片描述

trx_200需要等待trx_100更完毕后才能更新是因为:InnoDB使用锁来保证不会有脏写情况的发生,也就是在第一个事务更新了某条记录后,就会给这条记录加锁,另一个事务再次更新时就需要等待第一个事务提交了,把锁释放之后才可以继续更新。

有了版本链的记录,MySQL就可以通过当前事务id判断是在提交前读取记录,还是提交之后读取记录了。所以就有了ReadView的概念,实现版本控制

2. ReadView实现版本控制

ReadView的概念

这个ReadView中主要包含4个比较重要的内容:

  • m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
  • min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
  • max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值,是一个自增策略的ID。
  • creator_trx_id:表示生成该ReadView的事务的事务id。
    在读事务中有了ReadView记录,MySQL就可以根据当前活跃的事务存在的ids,以及ids中的最大id和最小id判断undo log中trx_id是否已经提交,是否在当前活跃事务中,从而实现版本的控制。

记录trx_id和ReadView字段关系总结

  • trx_id = creator_trx_id,当前事务访问自己修改过的记录,可访问
  • trx_id < min_tri_id,生成该版本的事务在当前事务生产ReadView已经提交,可访问
  • trx_id >= max_trx_id,生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
  • min_trx_id < trx_id < max_trx_id ,需要判断trx_id是否在m_ids中,存在则表示是当前正在活跃的事务不可访问,不存在则表示在生成ReadView的时候已经提交,可以访问。

ReadView进行版本控制主要是对可重复读和读已提交的隔离级别设计的,因为在读未提交直接读取最新版本即可,可串行化则是直接加锁访问。
而在READ COMMITTEDREPEATABLE READ隔离级别的事务来说,都必须保证读到已经提交了的事务修改过的记录,也就是说假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的。

所以在判断READ COMMITTED 和REPEATABLE READ 实现版本控制,只要读事务在生成ReadView的记录活跃ids不同即可。
READ COMMITTED中,开启两个事务trx_100的事务先修改记录,查询记录则ids=[100,200],然后提交trx_100事务。然后trx_200开始修改记录,查询记录生成ReadView的ids=[200],所以可以读取到trx_100提交的记录。前后读事务生成了两次ReadView。
REPEATABLE READ 中,开启两个事务trx_100的事务先修改记录,但只是在第一次生成ReadView。第二次读事务则不再生成新的ReadView。所以两次查询的接口都是一致的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值