『MySQL』跟面试官侃半小时MySQL事务隔离性,从基本概念深入到实现( 三 )

  • 如果版本号在事务数组array中 , 说明这个事务还没提交 , 所以 不可见;
  • 如果版本号不在事务数组array中 , 且低于高水位 , 说明这个事务已经提交 , 所以 可见;
  • 当然 , 无论什么时候 , 自己的事务id中的任何变化 , 都是可见的
  • 可以看看下面这个例子 , 更容易理解 。
    系统创建过的事务id:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
    事务A启动 , 拍个快照
    此时未提交的事务id有:7 , 8 , 9
    一致性视图:数组array[7,8,9] + 高水位16(15+1)
    对于任意一行数据的可见性判断如下:
    1)小于7的 , 可见
    2)大于16的 , 说明是快照后产生的 , 不可见
    3)10-15 , 不在数组array中 , 说明已经提交了 , 可见
    4)7 , 8 , 9在array中 , 说明未提交 , 不可见
    两个重要结论:
    • InnoDB 利用了“所有数据都有多个版本”的这个特性 , 实现了“秒级创建快照”的能力 。
    • MVCC的实现 , 就是根据当前事务的事务id为依据创建“一致性视图” , 利用一致性视图来判断数据版本的可见性 。
    3.隔离性实战 下面 , 我们来两个实战案例 , 将上面的基础概念与实现融会贯通吧 。
    1)并发select&update 案例
    id=1 的value初始为1 。
    『MySQL』跟面试官侃半小时MySQL事务隔离性,从基本概念深入到实现
    本文插图
    我们看下 , 在不同隔离级别 , Time5、Time7、Time9事务A查询到的value 分布为多少 。
    • “读未提交”:2 , 2 , 2
    • “读以提交”:1 , 2 , 2
    • “可重复读”:1 , 1 , 2
    • 串行化:1 , 1 , 2(注意 , 这里在事务A提交前 , 事务B都会阻塞 , 直到事务A提交后才能执行)
    2)并发update案例
    【『MySQL』跟面试官侃半小时MySQL事务隔离性,从基本概念深入到实现】id=1 的value初始为1 , 在可重复读级别:
    『MySQL』跟面试官侃半小时MySQL事务隔离性,从基本概念深入到实现
    本文插图
    我们看一下 , 你猜猜事务A和事务B读取的value是多少?
    答案是:1 和 3
    可能会产生困惑 , 事务A在启动后快照 , 所以读到了1是正常的 , 但是事务2在启动的时候快照了 , 然后在自己的事务中+1 , 怎么会读到3而不是2呢?
    原因很简单 , 即使是在可重复读的级别 , 事务 更新数据 的时候 , 只能用当前读(想想也能理解 , 不然update就出现数据不一致了) 。
    如果当前的记录的行锁被其他事务占用的话 , 就需要进入锁等待 。 而读提交的逻辑和可重复读的逻辑类似 , 它们最主要的区别是:在可重复读隔离级别下 , 只需要在事务开始的时候创建一致性视图 , 之后事务里的其他查询都共用这个一致性视图;在读提交隔离级别下 , 每一个语句执行前都会重新算出一个新的视图 。
    这里 , 我们需要注意的是事务的启动时机 。
    • begin/start transaction 命令并不是一个事务的起点 , 在执行到它们之后的第一个操作 InnoDB 表的语句 , 事务才真正启动,一致性视图是在执行第一个快照读语句时创建的 。
    • 如果你想要马上启动一个事务 , 可以使用 start transaction with consistent snapshot 这个命令 , 一致性视图是在执行 start transaction with consistent snapshot 时创建的 。
    4.关于幻读 首先明确一下 , 什么是幻读?开篇介绍了什么是幻读 , 这里再申明一下幻读出现的场景