以编程的思路来讲mvcc

  • 脏读
    • 事务1 更改数据a的值为B,但没有提交, 其他事务读取a的值是B, 那么这就是脏读
    • 危害:如果事务1 回滚了,那么其他事务读到的数据就是错误的。
  • 不可重复读
    • 事务1 第一次读取数据a的值是A,修改成C,提交事务。
    • 事务2 第一次读取数据a的值是A,在事务2提交以后重新读取数据a的值是C,这种情况叫不可重复读
    • 危害:
      • 某订单已支付完成,这时候会有两个事务同时执行
      1. 给用户发短信
        1. 未发货状态:短信1:支付成功,订单在配货中
        2. 已发货状态:短信2:订单已发货
      2. 给用户发货
      • 订单状态优先更改成了已发货,这时候短信服务发现状态是已发货,短信1就不会发送了
  • 幻读
    • 事务1 查询数据A不存在, 然后事务2, 插入了数据A, 这时候事务1再插入数据A, 会提示数据已存在,不能插入。 这就是幻读
    • 危害:唯一键冲突,其中一个事务需要回滚
      在这里插入图片描述

从Read committed到 Read read思路

要达到这样的目的:

处于事务中的查询,只能查到在创建事务之前commit的数据,不能查到之后commit的数据

解决思路:

  • 引入版本号概念,每次修改都把修改之前的数据作为老版本保存到版本库里面
  • 开启事务相当于创建了一个新版本,事务id == 版本id
  • 通过事务id查询不同的版本数据
  • 如果有数据C且C数据最后一次修改的事务id是c‘,简称Cc‘
    • 开启事务A,
      • 事务A的事务id tr_id = a‘
      • A保存c‘ 叫做事务A下的数据C的最后一次修改事务id,简称:C_tr_id = c’
    • 开启事务B,
      • 事务B的事务id tr_id = b‘
      • B保存c‘ 叫做事务B下的数据C的最后一次修改事务id,简称:C_tr_id = c’
    • 事务B修改了数据C,C_tr_id = b’,新数据变成了Cb’
    • 老数据Cc‘保存到版本库
    • 事务A读取数据C,从版本库 匹配 C_tr_id = c’, 查到Cc’
    • 事务B读取数据C,从版本库 匹配 C_tr_id = b’, 得到Cb‘
    • 事务B commit; 数据C的最后一次修改的事务id变成了b‘
    • 事务A读取数据C,由于C_tr_id并未更新,从版本库 匹配 C_tr_id = c’, 得到Cc‘

画个图

  1. 开启事务A和B,都指向数据C
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A9F0gz1e-1627491580414)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/cf76cba7-b963-49c6-ae4e-965ddba38137/Untitled.png)]

  2. 事务B修改了数据C,事务B修改之后事务F开启,事务F发现数据未commit,去查找上一版本,所以C_tr_id = c’
    在这里插入图片描述

  3. 事务B commit了,在事务B commit后开启事务G,由于已commit,事务G的C_tr_id = b’
    在这里插入图片描述

  4. 虽然实现了单个字段的多版本控制,但是如果要控制很多条数据, 就需要非常多的字段,显然这么设计不合理,所以要优化。从3总结出以下几点:

    1. c’对所有事务可见,因为c‘是最早的事务id,假设事务id是自增的,所以c‘是最小的事务id
    2. 事务B提交之后
      1. 因为事务A比事务B先开启,事务A对b‘不可见,所以a’ < b’
      2. 因为事务B比事务F先开启,事务F对b‘不可见, 所以要记录比事务F先开启的所有活跃事务集合,事务B显然在这个集合中的事务id都是不可见的
      3. 因为事务G在事务B后开启,且事务B已commit,所以事务B并不在当前活跃事务集合,所以事务G对b‘可见
    3. 所以开启事务之后要记录3个值
      1. 当前最小的活跃事务id
      2. 当前最大的活跃事务id
      3. 当前活跃队列集合

    然后修改成这样子了
    在这里插入图片描述

  5. 我把mvcc的一些名词对上号,我想你也可以写出来一个简化版的mvcc。PS:图中并非真实数据结构,只是模拟的!!!
    在这里插入图片描述

  6. 最后 从 Read read 到 Read committed要怎么做?

    只要每次读取的时候把 Read View 重置成最新的即可

    比如:事务A在事务B提交之后,在查询数据C,把 tr_max = h’, tr_list = a’,f’,g’,这样就变成了事务G的状态,可以读取到Cb‘了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值