zoukankan      html  css  js  c++  java
  • MVCC如何在RR上解决幻读

    一、InnoDB如何解决幻读

    • 幻读:在InnoDB的可重复度隔离级别下,使用当前读,一个事务前后两次查询同一个范围,后一次查询会看到期间新插入的行;
    • 幻读的影响:会导致一个事务中先产生的锁,无法锁住后加入的行,会产生数据一致性问题;
    • 产生幻读的原因:行锁只能锁住一行,不能避免新插入的记录;
    • 解决幻读:在两行记录之间加上间隙锁,阻止新纪录的插入,与间隙锁产生冲突的只有“往这个间隙插入记录”这个操作;
    • 同时添加间隙锁与行锁称为Next-key lock,注意间隙锁只有在InnoDB的可重复度隔离级别下生效;
    • MVCC只实现读取已提交和可重复读,InnoDB在可重复度的隔离级别下,使用MVCC+Next-key lock解决幻读;

    二、多版本并发控制MVCC

    1.基本思想

    • MVCC是InnoDB实现隔离级别的一种方式,用于实现读取已提交和可重复读两种隔离级别;
    • 对于读取未提交,直接读取最新版本的数据;
    • 对于串行化,使用加锁的方式访问记录;
    • 大多数事务型存储引擎实现都不是简单的行锁,基于并发性的考虑,一般会同时实现多版本并发控制(MVCC)处理读写冲突;
    • MVCC是乐观锁的一种实现,是通过保存数据在某一个时间点的快照实现的,写操作更新最新的版本,读操作读取旧版本;
    • MVCC中事务的修改操作(增删改)会为行记录新增一个版本快照,并把当前事务id写入trx_id;

    2.版本号

    • 系统版本号sys_id:每开始一个新的事务,系统版本号递增;
    • 在InnoDB中,聚簇索引记录中包含两个隐藏列:
      • trx_id:对记录进行改动时,trx_id会记录当前事务id,也就是当前系统版本号;
      • roll_pointer:对记录进行改动,会把旧版本记录写入undo日志,roll_pointer指向修改之前的版本;
    • 对同一条记录的更新,会把旧值放到一条undo日志中,作为一个旧版本的记录,多次更新之后这些版本会被roll_pointer连接成一个链表,称之为版本链

    3.版本读取

    • 对于读取已提交可重复读,就会用到版本链,关键在于怎么判断版本链中哪个版本对当前事务可见;
    • 使用ReadView(快照),ReadView是一个包含当前已经开始但是没有提交的事务的列表,记录每个事务的事务id,记最小事务id为min_id,最大事务id为max_id;
    • 版本比较规则:
    1. 如果记录版本的trx_id小于min_id,说明这个记录版本是已经被提交过的,对其他事务可见;
    2. 如果记录版本的trx_id大于max_id,说明这个记录版本是ReadView生成之后发生的,不能访问;
    3. 如果记录版本的trx_id在min_id和max_id之间,判断trx_id是否在ReadView中:
    • 如果在ReadView中,说明事务还未提交,该记录版本不可访问;
    • 如果不在ReadView中,说明该事务已经提交,该记录版本可以访问;
    • 如果当前记录版本不可读,就根据回滚指针roll_pointer找到旧版本的记录再进行判断;
    • 对于读取已提交,每次查询都会生成一个新的ReadView;
    • 对于可重复度,一个事务在第一次SELECT的时候生成一个ReadView,之后的查询复用这个ReadView;

    4.快照读与当前读

    • 快照读:MVCC中的SELECT操作是读取快照中的数据,不需要进行加锁;
    • 当前读:MVCC中修改数据的操作(增删改)需要进行加锁操作,从而读取最新的数据;

    5.例子

    • 假设当前有一个事务id为100的事务A,修改一个记录的name字段为name2,产生一个版本快照,因此有这样的版本链:
    • 假设事务A还没有提交,此时事务B进行SELECT,事务id为120,查询id为1的记录(记为第一次查询),此时生成ReadView为[100,120],根据版本读取规则,先找到trx_id为100的记录版本,发现不可读,于是通过回滚指针找到trx_id为60的记录,读取成功;
    • 当事务A提交之后,事务B再次进行SELECT查询id为1的记录(第二次查询),在读取已提交和可重复读两种隔离级别下有不同的情况:
      • 如果是读取已提交,则会创建一个新的ReadView为[120],此时读取trx_id为100的记录成功,也就是读取到了在事务期间提交的数据;
      • 如果是可重复读,则会使用第一次查询时的ReadView为[100,120],此时读取的是trx_id为60的记录,从而实现了可重复度;

     

    菜鸡的小疑惑:
    三级封锁协议不是可以解决脏读和不可重复度的问题吗,为什么要用MVCC来实现,是性能更好吗?
    转自:https://zhuanlan.zhihu.com/p/180350695
  • 相关阅读:
    4-1 zookeeper常用命令行操作
    3 zookeeper 基本数据模型
    2 ZooKeep安装
    1 zookeeper的特性
    js数据类型
    javascript中的严格模式
    2 Vue中组件开发
    1 Vue的基础语法
    责任链设计模式
    四中网络专线接入方式
  • 原文地址:https://www.cnblogs.com/tiancai/p/14009716.html
Copyright © 2011-2022 走看看