前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试系列-mvcc多版本并发控制机制

面试系列-mvcc多版本并发控制机制

作者头像
用户4283147
发布2022-10-27 15:56:05
3420
发布2022-10-27 15:56:05
举报
文章被收录于专栏:对线JAVA面试对线JAVA面试

mysql实现mvcc机制的时候,是基于undo log多版本链条+ReadView机制来做的,默认的RR隔离级别,就是基于这套机制来实现的,依托这套机制实现了RR级别,除了避免脏写、脏读、不可重复读,还能避免幻读问题;

undo log版本链

每条数据其实都有两个隐藏字段,一个是trx_id,一个是roll_pointer,这个trx_id就是最近一次更新这条数据的事务id,roll_pointer就是指向你了你更新这个事务之前生成的undo log;

undo log多版本链条实现的ReadView机制

执行一个事务的时候,就生成一个ReadView,里面比较关键的东西有4个:

  1. m_ids,这个就是说此时有哪些事务在MySQL里执行还没提交的;
  2. min_trx_id,就是m_ids里最小的值;
  3. max_trx_id,这是说mysql下一个要生成的事务id,就是最大事务id;
  4. creator_trx_id,就是你这个事务的id;

eg:

假设原来数据库里就有一行数据,很早以前就有事务插入过了,事务trx_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第一次查询这行数据,会走一个判断,就是判断一下当前这行数据的txr_id是否小于ReadView中的min_trx_id,此时发现txr_id=32,是小于ReadView里的min_trx_id就是45的,说明事务开启之前,修改这行数据的事务早就提交了,所以此时可以查到这行数据,接着事务B开始动手了,他把这行数据的值修改为了值B,然后这行数据的txr_id设置为自己的id,也是59,同时roll_pointer指向了修改之前生成的一个undo log,接着这个事务B就提交了,这个时候事务A再次查询,此时查询的时候,会发现一个问题,那就是此时数据行里的txr_id=59,那么这个txr_id是大于ReadView里的min_txr_id(45),同时小于ReadView里的max_trx_id(60)的,说明更新这条数据的事务,很可能就跟自己差不多同时开启的,于是会看一下这个txr_id=59,是否在ReadView的m_ids列表里,在ReadView的m_ids列表里,有45和59两个事务id,直接证实了,这个修改数据的事务是跟自己同一时段并发执行然后提交的,所以对这行数据是不能查询的,于是顺着这条数据的roll_pointer顺着undo log日志链条往下找,就会找到最近的一条undo log,trx_id是32,此时发现trx_id=32,是小于ReadView里的min_trx_id(45)的,说明这个undo log版本必然是在事务A开启之前就执行且提交的,那么就查询最近的那个undo log里的值好了,这就是undo log多版本链条的作用,他可以保存一个快照链条,让你可以读到之前的快照值。

Read Committed隔离级别是如何基于ReadView机制实现

每次查询都生成新的ReadView,那么如果在这次查询之前,有事务修改了数据还提交了,这次查询生成的ReadView里,那个m_ids列表当然不包含这个已经提交的事务了,既然不包含已经提交的事务了,那么当然可以读到人家修改过的值了;

eg:

假设我们的数据库里有一行数据,是事务id=50的一个事务之前就插入进去的,然后现在呢,活跃着两个事务,一个是事务A(id=60),一个是事务B(id=70),事务B发起了一次update操作,更新了这条数据,把这条数据的值修改为了值B,所以此时数据的trx_id会变为事务B的id=70,同时会生成一条undo log,由roll_pointer来指向,这个时候,事务A要发起一次查询操作,此时他一发起查询操作,就会生成一个ReadView,此时ReadView里的min_trx_id=60,max_trx_id=71,creator_trx_id=60,这个时候事务A发起查询,发现当前这条数据的trx_id是70。也就是说,属于ReadView的事务id范围之间,说明是他生成ReadView之前就有这个活跃的事务,是这个事务修改了这条数据的值,但是此时这个事务B还没提交,所以ReadView的m_ids活跃事务列表里,是有[60, 70]两个id的,所以此时根据ReadView的机制,此时事务A是无法查到事务B修改的值B的。接着就顺着undo log版本链条往下查找,就会找到一个原始值,发现他的trx_id是50,小于当前ReadView里的min_trx_id,说明是他生成ReadView之前,就有一个事务插入了这个值并且早就提交了,因此可以查到这个原始值,接着,咱们假设事务B此时就提交了,好了,那么提交了就说明事务B不会活跃于数据库里了,是不是?可以的,大家一定记住,事务B现在提交了。那么按照RC隔离级别的定义,事务B此时一旦提交了,说明事务A下次再查询,就可以读到事务B修改过的值了,因为事务B提交了;事务A下次发起查询,再次生成一个ReadView。此时再次生成ReadView,数据库内活跃的事务只有事务A了,因此min_trx_id是60,mac_trx_id是71,但是m_ids这个活跃事务列表里,只会有一个60了,事务B的id=70不会出现在m_ids活跃事务列表里了,此时事务A再次基于这个ReadView去查询,会发现这条数据的trx_id=70,虽然在ReadView的min_trx_id和max_trx_id范围之间,但是此时并不在m_ids列表内,说明事务B在生成本次ReadView之前就已经提交了。那么既然在生成本次ReadView之前,事务B就已经提交了,就说明这次你查询就可以查到事务B修改过的这个值了,此时事务A就会查到值B;

RR隔离级别如何基于ReadView机制实现

假设有一条数据是事务id=5的一个事务插入的,同时此时有事务A和事务B同时在运行,事务A的id是60,事务B的id是70,这个时候,事务A发起了一个查询,他就是第一次查询就会生成一个ReadView,此时ReadView里的creator_trx_id是60,min_trx_id是60,max_trx_id是71,m_ids是[60, 70],事务A基于这个ReadView去查这条数据,会发现这条数据的trx_id为50,是小于ReadView里的min_trx_id的,说明他发起查询之前,早就有事务插入这条数据还提交了,所以此时可以查到这条原始值的,接着就是事务B此时更新了这条数据的值为值B,此时会修改trx_id为70,同时生成一个undo log,而且关键是事务B此时他还提交了,也就是说此时事务B已经结束了,ReadView中的m_ids为[60,70],因为ReadView一旦生成了就不会改变了,这个时候虽然事务B已经结束了,但是事务A的ReadView里,还是会有60和70两个事务id,接着此时事务A去查询这条数据的值,他会惊讶的发现此时数据的trx_id是70了,70一方面是在ReadView的min_trx_id和max_trx_id的范围区间的,同时还在m_ids列表中,说明起码是事务A开启查询的时候,id为70的这个事务B还是在运行的,然后由这个事务B更新了这条数据,所以此时事务A是不能查询到事务B更新的这个值的,因此这个时候继续顺着指针往历史版本链条上去找接着事务A顺着指针找到下面一条数据,trx_id为50,是小于ReadView的min_trx_id的,说明在他开启查询之前,就已经提交了这个事务了,所以事务A是可以查询到这个值的,此时事务A查到的是原始值;

serializable隔离级别幻读如何基于ReadView机制实现

假设现在事务A先用select * from x where id>10来查询,此时可能查到的就是一条数据,而且读到的是这条数据的原始值的那个版本,现在有一个事务C插入了一条数据,然后提交了,接着,此时事务A再次查询,此时会发现符合条件的有2条数据,一条是原始值那个数据,一条是事务C插入的那条数据,但是事务C插入的那条数据的trx_id是80,这个80是大于自己的ReadView的max_trx_id的,说明是自己发起查询之后,这个事务才启动的,所以此时这条数据是不能查询的,因此事务A本次查询,还是只能查到原始值一条数据,所以大家可以看到,在这里,事务A根本不会发生幻读,他根据条件范围查询的时候,每次读到的数据都是一样的,不会读到人家插入进去的数据,这都是依托ReadView机制实现的;

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-08-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 对线JAVA面试 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • undo log版本链
  • undo log多版本链条实现的ReadView机制
  • Read Committed隔离级别是如何基于ReadView机制实现
  • RR隔离级别如何基于ReadView机制实现
  • serializable隔离级别幻读如何基于ReadView机制实现
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档