先看一下表结构和表里的数据:
再来看下面的操作:
说明:请注意上图中的事务隔离级别为RR,还有SQL执行顺序,先执行 SQL 1,然后执行 SQL 2,最后执行 SQL 3。
问题:为什么 SQL 2 能够正常执行,而 SQL 3 会被锁住呢?
下图中 Q2 和 Q3 都是幻读吗?
不可重复读和幻读到底有什么区别呢?
不可重复读是读取了其他事务更改的数据,针对update操作
幻读是读取了其他事务新增的数据,针对insert和delete操作
记录锁,是去锁住索引记录,所以记录锁也叫行锁,如果InnoDB存储引擎表在建立的时候没有设置任何索引,那么这时InnoDB存储引擎会使用隐式的主键来进行锁定。记录锁的栗子如下图所示:
间隙锁,锁定一个范围,但不包含记录本身,间隙锁是封锁索引记录中的间隔,或者第一条索引记录之前的范围,又或者最后一条索引记录之后的范围。
如果我们执行这样的SQL:SELECT * FROM user WHERE id > 5 AND id < 9 FOR UPDATE,会锁住(5,9)这个范围,如下图所示:
说明:
Next-Key Lock 是结合了 Gap Lock 和 Record Lock 的一种锁定算法(Next-Key Lock = Gap Lock + Record Lock
),InnoDB对于行的查询采用的就是这种锁定算法。Next-Key Lock 主要是为了解决幻读(Phantom Problem)的问题,Next-Key Lock 锁定的不是单个值,而是一个范围,是谓词锁(Predict Lock)的一种改进。
结合上图的数据来看一个具体的栗子:
说明:
为了方便大家理解,这里把加锁规则总结为三个“原则”和两个“优化”:
Next-Key Lock
Next-Key Lock = Gap Lock + Record Lock
EXPLAIN
EXPLAIN
,出门右转看 MySQL查询优化探秘ORDER BY
控制着索引查找的方向关于MySQL加锁分析的一般步骤是:先对应原则,再找优化。
下面所有的栗子都是使用最开始给出的表结构和数据,涉及到的 Next-Key Lock 分别为:
说明:因为+∞是开区间,所以InnoDB给每个索引加了一个不存在的最大值supremum,这样才符合前面说的“前开后闭”。
不存在等值查询条件的记录:
分析:
id=6
确定 Next-Key Lock 为 (5,10]存在等值查询条件的记录:
分析:
id=10
确定 Next-Key Lock 为 (5,10]id=10
这一条记录分析:
k=5
确定要加 Next-Key Lock 为 (0,5]如果将 lock in share mode
改成 for update
,就需要回表了,也就是说会锁住对应的主键索引,如下图所示:
下面两条SQL查询结果是相同的,但是加的锁不一样:
select * from t where id=10 for update;
select * from t where id>=10 and id<11 for update;
第1条SQL只会加 id=10
的记录锁,第2条SQL加锁情况如下图所示:
分析:
id>=10
这个条件跨了两个 Next-Key Lock,所以我们需要分开来分析id=10
这个条件最终会退化成记录锁,只锁住记录 id=10
id>10
和 id<11
属于同一个Next-Key Lock,由于不满足优化条件,需要加的锁还是 (10,15]id=10
和 临键锁 (10,15]再来看一个更普通的栗子:
分析:
还是先看看下面两条查询结果相同的SQL:
select * from t where k=10 for update;
select * from t where k>=10 and k<11 for update;
第1条SQL加的锁为 (5,10] 和 (10,15),第2条SQL加锁情况如下图所示:
分析:
k>=10
这个条件跨了两个 Next-Key Lock,所以我们需要分开来分析k=10
这个条件,上面已经分析了,这个条件加的锁为 (5,10] 和 (10,15)k>10
和 k<11
属于同一个Next-Key Lock,由于不满足优化条件,需要加的锁还是 (10,15]再来看一个更普通的栗子:
分析:
分析:
id>5
得到 Next-Key Lock 为 (5,10]、(10,15]、(15,20]、(20,25]、(25,supremum]limit 2
这个条件,可以进一步减少 Next-Key Lock,剩下 (5,10] 和 (10,15]如果去掉 ORDER BY,得到的加锁结果为 记录锁 id=15
、(15,20] 和 (20,25],显然跟上面加了 ORDER BY 的测试结果不相同。
分析:
上图右边的 update 语句已经被锁住了,接下来执行一条 insert 语句,如下图所示:
insert 语句执行成功,但是 update 语句报了一个死锁错误。
分析:
Next-Key Lock = Gap Lock + Record Lock
要表达的意思我们回过头来看看文章开头那个栗子,你能自己解释原因吗?
要分析这个问题,不能像上面那样静态的分析加了哪些锁,我们一定要理解间隙锁是一个动态的概念,它会随着记录而变化。再来看一个更有说服力的栗子:
分析:
id=10
的记录未被锁住,所以成功删除看了上面分享的内容,你学废了吗?有没有感觉自己以前是摸着黑做CRUD?