mysql幻读怎么办 mysql 幻读mvcc( 三 )


原则 2:查找过程中访问到的对象才会加锁 。
优化 1:索引上的等值查询,给唯一索引加锁的时候 , next-key lock 退化为行锁 。
优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁 。
一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止 。
举例来说明上述的原则:
建表
插入数据:
INSERT INTOt ( id ,c ,d ) VALUES (0, 0, 0);
INSERT INTOt ( id ,c ,d ) VALUES (5, 5, 10);
INSERT INTOt ( id ,c ,d ) VALUES (10, 10, 10);
【mysql幻读怎么办 mysql 幻读mvcc】INSERT INTOt ( id ,c ,d ) VALUES (15, 15, 15);
INSERT INTOt ( id ,c ,d ) VALUES (20, 20, 20);
INSERT INTOt ( id ,c ,d ) VALUES (25, 25, 25);
例子1:锁表
因为d字段上没有建索引,所以涉及该字段的查询加锁会锁住整个表
因为d字段上面没有建立索引,所以事务1执行后会导致整个表被锁 , 后面所有的操作都会在等待整个表锁被释放
例子2:主键/唯一索引 记录锁
id字段为主键,而且事务1查询命中了唯一的记录 , 默认是加Next-key Lock , 区间是(0 , 5],但是根据优化1,唯一索引/主键上的等值查询,会退化为行锁,所以只会锁5这个记录 。
例子3:主键/唯一索引上的间隙锁
由于表 t 中没有 id=7 的记录,所以用我们上面提到的加锁规则判断一下的话:根据原则 1,加锁单位是 next-key lock,事务1加锁范围就是 (5,10];同时根据优化 2,这是一个等值查询 (id=7),而 id=10 不满足查询条件,next-key lock 退化成间隙锁,因此最终加锁的范围是 (5,10),所以事务2会阻塞,事务3执行成功 。
例子4:普通索引上的间隙锁
c字段是普通索引,事务1执行时默认是对区间(0,5]加间隙锁,根据优化2,非唯一索引/主键会继续向右遍历,找到10,所以最终的加锁为(0 , 5]的Next-Key锁+(5,10)的间隙锁,所以事务2阻塞,事务3成功 。
例子5:间隙锁与行锁
事务1默认的Next-Key锁区间是(0,5],根据优化2会向右遍历 , 找到不满足查询条件的10,退化成间隙锁 , 所以事务1的锁是(0,5]的Next-Key锁+(5,10)的间隙锁 , 这两个锁与行锁是冲突的,而事务2申请的Next-Key锁是和事务1一样,但是c=5的行锁与事务1冲突,所以产生了阻塞,如果改为update t set d=1000 where c=6;因为此时产生的间隙锁为(5,10),而间隙锁与间隙锁是不冲突的,不会产生阻塞
例子6:lock in share mode锁覆盖索引
事务1存在覆盖索引的情况,不会去回表 , lock in share mode这种情况下只会锁c字段索引,而事务2是对主键加行锁 , 所以两者不存在冲突 。
例子7:主键/唯一索引上的范围查询
开始执行的时候,要找到第一个 id=10 的行,因此本该是 Next-Key Lock(5,10],根据优化 1, 主键 id 上的等值条件,退化成行锁,只加了 id=10 这一行的行锁 。范围查找就往后继续找 , 找到 id=15 这一行停下来 , 因此需要加 Next-Key Lock(10,15],所以事务3是冲突的 。
例子8:普通索引上的范围查询
开始执行时,找到第一个满足条件的行10,加锁Next-Key Lock(5,10],因为不是唯一索引,所以不会退化,继续向后面找,找到15这一行停下来,因此需要加 Next-Key Lock(10,15] , 因为是范围查询,所以锁不会退化 。
快照读: 通过MVCC实现 , 该技术不仅可以保证innodb的可重复读 , 而且可以防止幻读 , 但是他读取的数据虽然是一致的,但是数据是历史数据 。

推荐阅读