mysql是如何加锁的?
加锁默认是加临键锁,有特殊情况会优化为其他锁
索引上的等值查询:
- 唯一索引,给不存在的记录加锁时,优化为间隙锁
- 普通索引,向右遍历至最后一个不满足查询条件的值时吗退化为间隙锁
索引上的范围查询:
- 唯一索引:访问到不满足条件的第一个值为止
主键索引
ps:如果是非主键的唯一索引,那么会在大于等于时不会退化为记录锁,并且还会给主键索引加上记录锁
等值查询
范围查询
大于
大于等于
小于或小于等于
非唯一索引
等值查询
范围查询
MySQL的锁机制
在一次事务中加的所有锁,会在事务结束时一起释放。
MySQL中的锁:
根据锁的粒度可以划分为:
- 【全局锁】锁定数据库中的所有表,使其处于只读状态,常用在需要数据备份时
- 【表级锁】
- 【表锁】锁住整张表,粒度较大
- 【元数据锁】防止DML和DDL冲突所加的隐式锁
- 【意向锁】为避免加表锁时逐行查看行级锁的加锁情况这样的低效操作而加的隐式锁
- 【行级锁】
- 【记录锁】对表中的单条记录加锁。在读已提交和可重复读的事务隔离级别下都支持。
- 【间隙锁】对记录与记录之间的间隙加锁。只在可重复读的事务隔离级别下支持。间隙锁是开区间
- 【临键锁】对记录以及它与上一条记录之间间隙加的锁。临键锁是左开右闭区间
根据锁的容斥性,还可以分为共享锁S和独占锁X
根据锁的思想,还可以分为悲观锁和乐观锁
当前读和快照读:
-
【当前读】当前读中执行的sql语句都会加锁
下面四类语句是会加独占锁X的:
select ... from ... for update
insert ...
update ...
delete ...
-
【快照读】和MVCC相关,不加锁
读锁和写锁的兼容性问题:
【读锁】不排斥读锁,排斥写锁
【写锁】对读和写都排斥
注意,间隙锁锁定的是 索引记录之间的“间隙”,而不是锁定具体的记录。它的目的是防止其他事务在锁定的间隙中插入新的记录,从而避免 幻读。所以临键锁和间隙锁是可以同时存在的。
比如这里有两个事务查询:
事务A:
select * from easyjavademo.user where id = 5 for update;
事务B:
select * from easyjavademo.user where id > 6 for update;
6-9有临键锁,5-9有间隙锁,换句话说,间隙锁与间隙锁不冲突,这里可以不谈读写锁的兼容性问题
表1
意向锁:
为避免加表锁时逐行查看行级锁的加锁情况这样的低效操作而加的隐式锁。需要注意的是:意向锁是表级锁,是对整个表加的
比如:
当事务A执行一个update
语句时,会加目标记录加X型行锁,也会给整个表加X型意向锁(IX)
当事务B想给表加表 锁,会先查看表中有无意向锁,如果没有,则可以直接加表锁;如果有,则要考虑事务B要加的锁与当前意向锁是否兼容,兼容方可加。
加锁分析
如何看一个事务加了什么锁?
select * from performance_schema.data_locks;
其中:
-
lock_type
字段说明锁的粒度,如全局锁、表级锁、行级锁; -
lock_mode
说明锁的类型。加锁默认一般是临键锁,有特殊情况会优化为其他锁,字段说明如下:S
、X
这样无特殊说明的,就是临键锁REC_NOT_GAP
这样,意为没有间隙记录,说明加的是记录锁GAP
说明加的是间隙锁INSERT_INTENTION
插入意向锁
-
index_name
说明这个锁加在了哪个字段上 -
lock_data
被锁住的区间右值,即上界
案例:
对于上面的表一:(id主键,sort二级唯一索引)
各语句加锁情况如下: