目录
加锁的规则
前提:可重读读的事务隔离下
原则 1 : 加锁的基本单位
是Next key Lock ,即 :间隙锁 + 行锁
原则 2 : 访问过的对象都会进行加锁
优化 1 : 索引上的等值查询
,给唯一索引加锁
时,会退化为行锁
优化 2 : 索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,退化为间隙锁
一个Bug :唯一索引
上的范围查询会访问到不满足条件的第一个值为止
锁是加在索引
上面的
大于值,就从该值之后去寻找,等于该值,就从该值前一个值寻找
“间隙”,其实根本就是由“这个间隙右边的那个记录”定义的,比如从右往左扫描,扫到10,加的锁范围就是(5,10]
通过explain的结果,就能够脑补出一个SQL语句的执行流程
主键索引范围锁
非唯一索引范围锁
唯一索引范围锁bug
非唯一索引上存在”等值”的例子
limit语句加锁
一个死锁的例子
不等号条件的等值查询
begin;
select * from t where id > 9 and id < 12 order by id desc for update;
-- 加锁范围是主键索引上的 (0,5]、(5,10]和(10, 15)
-- Order by id desc 是从大到小进行加锁的
这里面也是有等值查询的,对于id < 12 ,引擎内部,找的是id = 12 这个值,只是最终没找到,找到了(10, 15)的间隙,之后就进行向左遍历,遍历过程中,不是等值查询,会扫描到id = 5,会加一个(0,5],因为加锁基本单位为Next Key
锁是“在执行过程中一个一个加的”,而不是一次性加上去的
锁等待
Session A | Session B |
---|---|
begin; select * from t where id > 10 and id <= 15 for update | xx |
xx | delete from t where id=10(Query OK)insert into t values(10, 10, 10)blocked |
delete删除了锁的间隙就变大了,原来是(5,10],(10,15] 删除完后就变为(5,15]
id > 10 于是从10以后开始找
update
Session A | Session B |
---|---|
begin; select c from t where c > 5 lock in share mode | xx |
xx | update t set c = 1 where c = 5 (Query OK) update t set c = 5 where c = 1(blocked) |
加锁:(5, 10],(10,15]..(25,+]
要把c=5改成c=1,你可以理解为两步:
- 插入(c=1, id=5)这个记录
- 删除(c=5, id=5)这个记录