我对 MySQL 锁、事务、MVCC 的一些认识( 三 )


Repeatable read 隔离级别能解决不可重复读根本原因其实就是 read view 的生成机制和 Read committed 不同 。

  • Read committed :只要是当前语句执行前已经提交的数据都是可见的 。
  • Repeatable read :只要是当前事务执行前已经提交的数据都是可见的 。
不像 Read committed , 在 Repeatable read 的隔离级别下 , 创建事务的时候 , 就生成了当前的 global read view,一直维持到事务结束 。这样就能实现可重复读 。
幻读与 Next-Key 锁当前读与快照读通过 MVCC 机制 , 虽然让数据变得可重复读 , 但我们读到的数据可能是历史数据 , 是不及时的数据 , 不是数据库当前的数据!对于这种读取历史数据的方式 , 我们叫它快照读 (snapshot read) , 而读取数据库当前版本数据的方式 , 叫当前读 (current read) 参考[3]
  • 快照读:就是select
    • select * from table ….;
  • 当前读:特殊的读操作 , 插入/更新/删除操作 , 属于当前读 , 处理的都是当前的数据 , 需要加锁 。
    • select * from table where ? lock in share mode;
    • select * from table where ? for update;
    • insert;
    • update ;
    • delete;
解决幻读为了解决当前读中的幻读问题 , MySQL事务使用了 next-key lock。
我对 MySQL 锁、事务、MVCC 的一些认识

文章插图
 
Repeatable read 通过 next-key lock 机制避免了幻读现象 。
InnoDB存储引擎有3种行锁的算法 , 分别是:
  • Record Lock: 单个记录上的锁
  • Gap Lock: 间隙锁 , 锁定一个范围 , 但不包括记录本上
  • Next-Key Lock: Gap Lock + Record Lock
next-key lock 是行锁的一种 , 实现相当于 record lock(记录锁) + gap lock(间隙锁);其特点是不仅会锁住记录本身( record lock 的功能) , 还会锁定一个范围( gap lock 的功能) 。
当InnoDB扫描索引记录的时候 , 会首先对索引记录加上行锁(Record Lock) , 再对索引记录两边的间隙加上间隙锁(Gap Lock) 。加上间隙锁之后 , 其他事务就不能在这个间隙修改或者插入记录 。
当查询的索引含有唯一属性的时候 , Next-Key Lock 会进行优化 , 将其降级为Record Lock , 即仅锁住索引本身 , 不是范围 。
下图引用自 云栖社区[4]
我对 MySQL 锁、事务、MVCC 的一些认识

文章插图
 
参考资料[1]
mysql 5.7文档:


推荐阅读