沫言|解读数据库:深入分析MySQL中事务以及MVCC的实现原理( 四 )


LBCC方案中 , 如果我们的业务系统是读多写少的话 , 这种方案就会极大影响了效率 , 所以我们就有了另一种解决方案:MVCC 。
MVCCMVCC,多版本的并发控制 , 英文全称:Multi Version Concurrency Control 。 就是当我们在修改数据的时候 , 可以为这条数据创建一个快照 , 后面就可以直接读取这个快照 。
那么MVCC具体到底是如何实现的呢?
为了实现MVCC机制 , InnoDB内部为每一行添加了两个隐藏列:DB_TRX_ID和DB_ROLL_PTR(MySQL另外还有一个隐藏列DB_ROW_ID , 这是在InnoDB表没有主键的时候会用来作为主键 , 想详细了解可以点击这里) 。
DB_TRX_ID长度为6字节 , 存储了插入或更新语句的最后一个事务的事务ID 。
DB_ROLL_PTR长度为7字节 , 称之为:回滚指针 。 回滚指针指向写入回滚段的undo log记录 , 读取记录的时候会根据指针去读取undo log中的记录 。
正因为MySQL中undo log中会维护一个历史数据记录 , 所以我们应该养成定期提交事务的习惯 , 否则回滚段会越来越大 , 甚至占满了表空间 。
快照读快照读是针对上文的当前读而言 , 指的是在RR隔离级别下 , 在不加锁的情况下MySQL会根据回滚指针选择从undo log记录中获取快照数据 , 而不总是获取最新的数据 , 这也就是为什么另一个事务提交了数据 , 在当前事务中看到的依然是另一个事务提交之前的数据 。
MySQL什么时候开始读取快照我们先看看MySQL默认隔离级别RR下的一个例子(注意 , test和test2两张表一开始都是空表 , 均只有id和name两个字段) 。

  • 场景1(事务1操作数据之后再进行第一次查询):

沫言|解读数据库:深入分析MySQL中事务以及MVCC的实现原理
  • 场景2(事务1不进行任何操作 , 事务2先开始第一次查询)

沫言|解读数据库:深入分析MySQL中事务以及MVCC的实现原理通过上面两个场景中我们可以得出结论:RR隔离级别快照并不是在BEGIN就开始产生了 , 而是要等到事务当中的第一次查询之后才会产生快照 , 之后的查询就只读取这个快照数据
  • 场景3(事务2先进行一次t1表查询之后 , 事务1再去操作其他表t2)

沫言|解读数据库:深入分析MySQL中事务以及MVCC的实现原理从场景3我们可以得出结论:RR隔离级别快照并不只是针对当前所查询的数据 , 而是针对当前MySQL中的所有数据(跨库也一样 , 只要在同一个MySQL)
MVCC查询机制MVCC机制到底如何查询的呢?假设由很多个事务同时进行 , 那么就会产生很多快照 , 查询的时候又到底是怎么做的呢?
接下来我们把抽象的概念具体化 , 假定DB_TRX_ID和DB_ROLL_PTR均为整型 , 接下来我们进行查询演示:
1、清空原先的test表 , 事务A插入两条数据 , 此时DB_TRX_ID(事务id)为1,DB_ROLL_PTR(回滚指针为null)
沫言|解读数据库:深入分析MySQL中事务以及MVCC的实现原理2、这时候事务B进行了一次查询 , 会得到上面的结果 , 事务2还没提交的时候又来了事务C , 事务C插入了id=3的数据 , 此时表中的数据如下:
沫言|解读数据库:深入分析MySQL中事务以及MVCC的实现原理注意 , 这时候第3条数据的事务id为3 , 因为事务2也会产生一个事务id3、这时候事务B再次进行查询 , 根据上面了解的 , 我们知道 , 这时候应该是查询不出王五的 , 所以实际上二次查询可能是这么查的: