传统mvcc和innodb mvcc的实现方法

发布时间:2021-06-25 10:33:10 作者:chen
来源:亿速云 阅读:186

这篇文章主要介绍“传统mvcc和innodb mvcc的实现方法”,在日常操作中,相信很多人在传统mvcc和innodb mvcc的实现方法问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”传统mvcc和innodb mvcc的实现方法”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

        根据《高性能mysql》一书的描述,mvcc实现应该大致如下:

        1、每行数据后面有两个隐藏的字段,一个字段为更新标识,一个字段为删除标识,记录内容为更新/删除时候的系统版本号。

        2、select过程中,会根据当前事务版本号去过滤小于等于的数据。

        3、update操作实际会新增一行记录,而并非在原数据上修改。

        4、delete也并非物理删除,而是在删除标识字段上加上版本号。

        然而innodb的mvcc并不尽相同!!

        在innodb中,两个隐藏字段分别为DATA_TRX_IDDATA_ROLL_PTR(如果没有主键,则还会多一个隐藏的主键列,在一些文章中有看到del_flag字段,但是具体它是一个单独字段还是DATA_TRX_ID中的一位,目前还没有搞清楚)。

        DATA_TRX_ID

记录最近更新这条行记录的事务 ID,大小为 6 个字节

        DATA_ROLL_PTR

表示指向该行回滚段(rollback segment)的指针,大小为 7 个字节,InnoDB 便是通过这个指针找到之前版本的数据。该行记录上所有旧版本,在 undo 中都通过链表的形式组织,在网上搜了一下,大概格式如下,它记录的是每个版本被修改的数据,并非是一条操作的反操作。

传统mvcc和innodb mvcc的实现方法

所以,实际上每次进行update操作时,并非是会创建一个新的副本。而是会先将就的数据行copy进undo log中去,随后修改该数据行,并且修改其DATA_TRX_ID 和 DATA_ROLL_ID。

        上面说了支撑mvcc功能的列结构,那么下来看一下mysql的四种事务隔离级别和mvcc的关系。

        1.脏读

        和mvcc没有关系,每次select最新的数据,别的事务的数据只要已经物理写入或者修改,那么它便会去读,直接忽略掉后面的DATA_TRX_ID。

        2.提交读

        在mvcc的使用上,innodb引入了ReadView 这一方式。在生成一个ReadView的时候,会收集所有目前所有活跃事务(既还未提交的事务)的版本号到一个队列中m_ids,并且把当前系统版本号+1,也加入队列中。那么就可以找到版本号的最小值up_limit_id和最大值low_limit_id。在select的过程中,对于一条数据,与m_ids对比,会有如下结果。

        a.小于up_limit_id,说明该事务在ReadView开启之前已经提交,所以为有效数据。

        b.大于low_limit_id,说明该事物是在ReadView开启之后才提交的,为无效数据,这时候就要顺着undo log链表向上查找了,直到找到有效的

        c.如果 up_limit_id<id<=low_limit_id,这个时候就要拿到id在m_ids中做比较,如果找到了相同的,则说明ReadView建立时,该事务还未提交,所以为无效数据,如果找不到,则为有效数据。数据无效处理方法同b。

        ps.在undo log上查找时,并不是只有data_trx_id小于up_limit_id才为有效,满足条件c也是可以的。

        那么解释了innodb如何使用mvcc,那么提交读就好理解了,它在每次select都会新建一个ReadView,所以两次select会拥有不同的ReadView,那么两次对于同一个数据的结果是可能不同的。

        3.可重复读

       同样用mvcc的可重复读就更好解释了,可重复读只在第一次select的时候建立一次ReadView,在整个事务的周期内都使用这个ReadView。所以它就能保证对于同一数据查询总是相同版本号的数据。

        4.序列化

        实际上幻读并非是传统意义的上的两次读取结果不一致,因为从可重复读的场景上来看。即使是有新的数据插入,因为mvcc的原因实际上并不会对两次的结果产生影响。幻读多发于 读-写 操作,既select * from t1 where id = 1 然后插入id = 1 的数据,此时有别的事务插入了id = 1,结果报错,这是mvcc无法解决的问题,所以采取的方案便是序列化执行。

        最后说一下两种数据丢失

第一类丢失更新(回滚丢失,Lostupdate) (通过设置隔离级别可以防止 Repeatable Read)

    这类问题产生原因在于由于回滚使得别的事务提交的数据被一并抛弃掉了。

第二类丢失更新(覆盖丢失/两次更新问题,Second lost update) A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失:   

    这类问题实际上是由于读取、更新并非原子操作,在执行过程中,可能已经别别的事务修改掉了,所以这个时候你用的是一个旧的数据进行更新,最终导致数据出现错乱。比如说另外一个已经修改为1100,你再来进行修改为1000的基础上加100,那么这样就丢失了部分数据。

    第一类问题可以通过数据库的隔离级别来解决掉,对于第二类就需要在业务代码中进行了,比如采用select for update这样的操作,或者加上时间版本号来防止问题的发生。

到此,关于“传统mvcc和innodb mvcc的实现方法”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

推荐阅读:
  1. PostgreSQL的MVCC vs InnoDB的MVCC
  2. InnoDB MVCC实现原理及源码解析

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

mvcc

上一篇:php如何实现异步将远程链接上内容写到本地

下一篇:PHP如何实现Google plus的好友拖拽分组效果

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》