MVCC多版本并发控制机制原理

一、mysql的事务隔离级别

1、read uncommitted(读未提交)

read uncommitted隔离级别可以防止脏写。俩个事务在没提交的情况下去更新同一行数据的值,但是在这种隔离级别下,可能发生脏读、不可重复读、幻读。

2、read committed(简称RC,读已提交)

read committed可以防止脏读和脏写。事务没有提交的情况下去修改值,是绝对读不到的。但是,可能会发生不可重复读和幻读的情况,因为一旦其他事务修改了值并提交的事务,你的事务是会读到的,所以可能你多次读到的值是不同的。

3、repeatable read(简称:RR,可重复度,mysql的默认隔离级别)

repeatable read可以解决脏读、脏写和不可重复读的问题。你一个事务多次查询一个数据的值,哪怕别的事务修改了这个值还没提交,你不会读到别人提交事务修改的值,你的事务一旦开始,多次查询一个值,会一直读到同一个值。

4、serializable(简称:串行读)

serializable可以解决所有问题,因为这种隔离级别不允许多个事务并发的执行,只能串行起来执行。

二、undo log版本链

每条数据都有俩个隐藏的字段,一个是trx_id,另一个是roll_pointer,trx_id是最近一次更新这条数据的事务id,roll_pointer就是指向了你更新这个事务之前生成的undo log。举个例子:假设事务A(id=50),插入了一条数据,插入的这条数据的值是A。因为事务A的id是50,所以这条数据的trx_id=50,roll_pointer指向一个空的undo log,因为之前这条数据是没有的。

接着事务B(id=58)把这条数据修改为B,那么此时会生成一个新的undo log来记录之前的值,然后让roll_pointer指向这个实际的undo log回滚日志。

接着事务C(id=69)把这条数据修改为C,此时会把数据行里的trx_id改为69,roll_pointer指向了本次修改之前生成的undo log,然后生成一条undo log记录之前事务B修改的那条记录。

看到上面的例子大家应该清楚了什么是undo log的版本链了吧。就是多个事务串行执行的时候,每个人修改了一行数据,都会更新隐藏字段trx_id和roll_pointer,同时之前多个数据快照对应的undo log,会通过roll_pointer指针串联起来,形成一个重要的版本链

三、Read View

readView简单来说,就是你执行一个事务的时候,就给你生成一个readView,里面由四个部分组成:

  • m_ids:这个就是说此时有哪些事务在mysql里执行还没提交

  • min_trx_id:就是m_ids中最小的值

  • max_trx_id:这是mysql下一个要生成的事务id,就是最大的事务id

  • creatot_trx_id:当前事务的id

例子:假设原来数据库中有一行数据,事务id=32,他的值就是初始值。接下来俩个并发过来执行了,一

个是事务A(id=45),一个是事务B(id=59),事务B是要去更新这行数据的,事务A去读取这行数据,俩个事务如下图所示:

现在事务A直接开启一个ReadView,这个ReadView里的m_ids就包含了事务A和事务B的俩个id,45和59,然后min_trx_id就是45,max_trx_id就是60,creator_trx_id就是45,是事务A自己。这个时候事务A第一次查询这行数据,会走一个判断:就是判断一下当前这行数据的trx_id是否小于ReadView中的min_trx_id,此时发现trx_id=32,是小于ReadView里的min_trx_id(45),说明你事务开启之前,修改这行数的事务早就提交了,所以此时可以查到这行数据,如下图所示:

接着事务B把这行数据修改为了B值,然后这行数据的trx_Id设置为自己的id,就是59,同时roll_pointer指向了之前修改之前生成的undo log,接着这个事务B就提交了。

这个时候事务A再次查询,此时会发现一个问题,那就是此时数据行里的trx_id=59,那么这个trx_id是大于ReadView里的min_trx_id=45,同时小于ReadView里的max_trx_id=60,说明更新这条数据的事务可能和自己差不多同时开启的,接下来查看ReadView里的m_ids列表。结果在ReadView的m_ids列表里有45和59俩个事务id,所以对这行数据是不能查询的。既然不能查询这条数据,那查什么呢?简

单,那么就查询最近的那个undo log里的值,这就是undo log多版本链条的作用,他可以保存一个快照链条,让你可以读到之前的快照值,如下图:

通过ReadView+undo log日志链表的机制,就可以保证事务A不会读到并发执行的事务B更新的值,只会读到之前最早的值。

接着假设事务A自己更新了这行数据的值,改为了值A,trx_id修改为45,同时保存之前事务B修改的值的快照。

此时事务A来查询这条数据的值,会发现这个trx_id=45,居然跟自己的ReadView里的creator_trx_id(45)一样的,说明这行数据就是自己修改的,自己修改的值自己自然可以看到。

接着事务A执行的过程中,突然开启一个事务C,这个事务的id=78,然后他更新了那行数据的值为C,还提交了,

这个时候事务A再去查询,会发现当前数据的trx_id=78,大于了自己的ReadView中的max_trx_id=60,说明事务A开启之后,又有了一个新的事务修改了数据,自己当然不可以看到。

此时就会顺着undo log多版本链条往下找,自然找到事务A自己之前修改的过的那个undo log版本链,因为那个trx_id=45跟自己的ReadView里的creator_trx_id是一样的,所以此时直接读取自己之前修改的那个版本。

通过上面的例子,我相信大家应该明白了mysql通过这套机制(ReadView+undo log日志版本链)如何实现了MVCC多版本并发控制机制。

借道友法力一用:

========================== stay hungry stay foolish =============================

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MySQL的多版本并发控制MVCC)是一种并发控制机制,它主要是为了解决并发读写冲突的问题。在MVCC机制中,每个事务都可以看到数据库中的一个快照,这个快照是在事务开始时确定的。事务读取数据时,实际上是读取了该快照中的数据,而不是实际的数据。当事务需要修改数据时,MySQL会根据数据的版本号来判断是否可以进行修改。 MVCC的实现原理主要是在每一行数据后面保存多个版本号,并且还需要保存该版本号对应的事务ID。当开始一个事务时,MySQL会为该事务分配一个唯一的事务ID,该事务ID会被用于标记事务对应的数据版本号。当一个事务需要读取数据时,MySQL会根据该事务的事务ID和版本号来判断是否允许读取该数据。如果该事务的事务ID小于等于该数据的版本号,那么就可以读取该数据。如果该事务需要修改数据,则MySQL会为该数据在数据库中创建一个新版本,并将该新版本版本号和事务ID保存下来。这样,其他事务就可以继续读取原来的版本,而该事务则可以读取新版本并修改数据,从而实现并发控制。 需要注意的是,MVCC只能解决读写冲突的问题,而不能解决写写冲突的问题。此外,MVCC也会占用一定的存储空间,因为每个数据行都需要保存多个版本号和事务ID。因此,在使用MVCC机制时,需要注意存储空间和性能方面的问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值