说明:本栗子所用为mysql 8.0 + ; 隔离级别为默认的RR级别
按锁的粒度来分,有表锁、行级锁、页锁;这里主要讲行级锁
按兼容性来分,有排它锁和共享锁:
- 排它锁:又叫X锁和写锁;事务对资源加了排他锁,其他事务就不能再给它加任何锁了
- 共享锁:又叫S锁和写锁;事务对资源加了共享锁,其他事务不能加排它锁,但能加共享锁
·手动加锁:
- 手动加共享锁:SELECT …
LOCK IN SHARE MODE
- 手动加排它锁:SELECT …
FOR UPDATE
·当前读与快照读:
- 快照读:就是普通的select语句,快照读是不会对数据上锁的
- 当前读:读取的是最新版本数据, 并且对读取的记录加排他锁;像update , delete , insert,lock in share mode,for update语句
InnoDB默认是支持行级锁的,包括以下几种模式:
- 记录锁(Record Lock):对索引项加锁,锁定符合条件的行
- 间隙锁(Gap Lock):对索引项之间的“间隙”加锁,锁定记录的范围(开区间)
- 临键锁(Next-key Lock):锁定索引项本身和索引范围(左开右闭),结合了记录锁和间隙锁;
- 意向锁(Intention Lock):是一种表级锁,但不与行级锁冲突;
InnoDB存储引擎中默认使用的是Next-Key Lock,但在一些场景下会退化成记录锁或间隙锁
- 本栗子实验,起始数据都为0,5,10,15,20;每次实现完恢复数据,再进行实验;
- 此范围内临建锁有(-∞,0] ,(0,5] ,(5,10],(10,15],(15,20],(20,+∞]
- 对于当前读,当条件数据存在时,「临建锁 」会退化成「记录锁」
- 对于当前读,当条件数据不存在时,「临建锁 」会退化成「间隙锁」
【条件数据存在】:
- 对id=5的行更新,加上了X锁,别的事务不可以对该行操作,但可以对其他行操作(更新id=10的数据)
·
`【条件数据不存在】:
- 对id=8的行加X锁,原本是间隙锁(5,10 ] ,由于没有记录,退化成间隙锁(5,10);别的事务不能对该间隙进行写操作,故插入id=6阻塞:
·
- 对于当前读,锁的范围是条件范围所在的「临建锁」并集,起始范围可能会退化成记录锁,并止于往后第一个不匹配范围的数据所在临建锁
- 我实验的结果和别人的不太一样,别人的是止于间隙锁
- 比如>3 and id <15,条件范围的临建锁有(0,5] ,(5,10],(10,15],所以最终锁的范围是(0,15]
·- 如果是>=5 and id <=15,条件范围的临建锁有5 ,(5,10],(10,15],(15,20],所以最终锁的范围是[5,20]
- id=5的数据满足条件,临建锁退化成记录锁5;id=15的数据满足条件,所以要往后查找到不满足的记录20才停止加锁
·
对age字段加上普通索引
- 对于当前读,当条件数据存在时,除了加条件所在的「临建锁」还会加上往后第一个不匹条件的数据所在「间隙锁」
- 对于当前读,当条件数据不存在时,条件所在的「临建锁 」会退化成「间隙锁」
【条件数据存在】:
- 对age=15的行更新,加上了(10,15]的临建锁,往后第一个不匹配的数据是age=20,所以还会加上(15,20)间隙锁,所以锁的范围是(10,20)
·
【条件数据不存在】:- 对age=13的行更新,加上了(10,15]的临建锁,但条件数据不存在,所以退化成(10,15)间隙锁
·
- 对于当前读,锁的范围是条件范围所在的「临建锁」并集,起始范围可能不会退化成记录锁,并止于往后第一个不匹配范围的数据所在临建锁
- 比如age>8 and age <20,条件范围的临建锁有(5,10] ,(10,15],(15,20],所以最终锁的范围是(5,20]
··- 比如age>=10 and age <=20,条件范围的临建锁有(5,10] ,(10,15],(15,20],(20,25]所以最终锁的范围是(5,20]
- id=10的数据满足条件,普通索引临建锁不会退化成记录锁;id=20的数据满足条件,所以要往后查找到不满足的记录25才停止加锁
·
- 对于当前读,条件没有索引,会导致行级锁升级为表锁
name字段是没有索引的,不管数据存不存在,行级锁会升级为表锁
·
【对于当前读,等值匹配条件数据存在时】
- 唯一索引会将「临建锁 」会退化成「记录锁」
- 非唯一索引「临建锁 」不会退化,并且还会加上往后第一个不匹条件的数据所在「间隙锁」
·【对于当前读,等值匹配条件数据不存在时】
- 唯一索引和非唯一索引「临建锁 」都会退化成「间隙锁」
·【对于当前读,条件范围匹配】
- 锁的范围是条件范围所在的「临建锁」并集,并止于往后第一个不匹配范围的数据所在临建锁
- 唯一索引的起始范围可能会退化成记录锁,非唯一索引的起始范围不会退化成记录锁
·【对于当前读,条件字段没有索引对行级锁的影响】
- 会导致行级锁升级为表锁,使用时尤为注意
以上是基于mysql 8.0 + ,默认RR级别,索引对行级锁影响得实验结论,如有错误,欢迎指正