剑指Offer(SQL)——InnoDB可重复读隔离级别下如何避免幻读

1、表象:快照读(非阻塞读)——伪MVCC

读取数据也是有规范的,分为当前读和快照读。。。

当前读:加了锁的CRUD事务,因为读取的实际上就是最新的版本,并且在读取之后还不允许其他事务修改查询的结果就是像select * from tablename lock in share modeselect * from tablename for update 这种加了共享锁和排它锁的叫做当前读。。。

RDBMS主要是由两大部分组成的:MySQL程序和存储引擎InnoDB。
剑指Offer(SQL)——InnoDB可重复读隔离级别下如何避免幻读
update和delete都会通过where进行不断的遍历条件,每遍历一个条件查询到的数据都会加锁,直到所有条件都被遍历完然后返回。而insert会触发唯一键的查询,同样也会触发当前读。。

快照读:就是简单的指不加锁的非阻塞的select操作。但是不加锁的前提就是事务隔离级别不为Serializable的条件下才成立的,在Serializable事务隔离级别下因为是串行化查询,因此会退化成当前读即select ... lock in share mode。。

快照读简单来说是为了在多线程情况下提升并发性能,快照读是基于多版本并发控制,认为MVCC是行级锁的一个变化,在很多情况下避免了加锁操作开销自然会降低,但是缺点在于可能获取的数据不是最新的数据。

实现InnoDB下的快照读

下面来说一下在READ-COMMITTED和REPEATABLE-READ级别下的InnoDB的非阻塞度是如何试实现的。

实际上在InnoDB存储数据的时候,还会额外存储三个不显示出来的字段:DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID,下面简单介绍一个各字段的含义。

  • DB_TRX_ID:最后一次修改本行记录的事务ID
  • DB_ROLL_PTR:回滚指针,指向这条记录上的一个版本(存储于rollback segment中)
  • DB_ROW_ID:隐含的自增ID,如果数据表没有主键,InnoDB会自动DB_ROW_ID产生一个聚簇索引

undo日志:逻辑日志,记录时间点为修改缓冲中页面之前。。。
剑指Offer(SQL)——InnoDB可重复读隔离级别下如何避免幻读

内在:next-key(行锁和Gap锁)

  • 行锁

行锁就是对行上锁。

  • Gap锁

Gap锁:索引树中插入新纪录的空隙
Gap锁:指一段距离将插入的索引占用的空隙用锁包住,Gap锁目的就是防止事务因为两次当前读出现幻读的情况,但是READ-COMMITTED及以下的级别都没有Gap锁因此无法避免幻读。。。

REPEATABLE-READ级别下,无论是删改如果要使用到主键索引或者唯一索引那么问题是还用到Gap锁吗???

其实这个要根据情况而定,where条件全部命中就不需要使用Gap锁否则就需要使用Gap锁。。。