MYSQL锁机制 - 锁的简述 | 索引对行级锁的影响

Posted 做猪呢,最重要的是开森啦

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MYSQL锁机制 - 锁的简述 | 索引对行级锁的影响相关的知识,希望对你有一定的参考价值。

说明:本栗子所用为mysql 8.0 + ; 隔离级别为默认的RR级别

0. 锁的分类:

按锁的粒度来分,有表锁、行级锁、页锁;这里主要讲行级锁

按兼容性来分,有排它锁和共享锁:

  • 排它锁:又叫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,但在一些场景下会退化成记录锁或间隙锁

1. 主键/唯一索引对行级锁的影响:

  • 本栗子实验,起始数据都为0,5,10,15,20;每次实现完恢复数据,再进行实验;
  • 此范围内临建锁有(-∞,0] ,(0,5] ,(5,10],(10,15],(15,20],(20,+∞]
1.1. 唯一索引等值条件匹配场景:
  • 对于当前读,当条件数据存在时,「临建锁 」会退化成「记录锁」
  • 对于当前读,当条件数据不存在时,「临建锁 」会退化成「间隙锁」

【条件数据存在】:

  • 对id=5的行更新,加上了X锁,别的事务不可以对该行操作,但可以对其他行操作(更新id=10的数据)
    ·
    `

【条件数据不存在】:

  • 对id=8的行加X锁,原本是间隙锁(5,10 ] ,由于没有记录,退化成间隙锁(5,10);别的事务不能对该间隙进行写操作,故插入id=6阻塞:
    ·
1.2. 唯一索引条件范围匹配场景:
  • 对于当前读,锁的范围是条件范围所在的「临建锁」并集,起始范围可能会退化成记录锁,并止于往后第一个不匹配范围的数据所在临建锁
  • 我实验的结果和别人的不太一样,别人的是止于间隙锁
  • 比如>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才停止加锁
    ·

2. 非唯一索引对行级锁的影响:

对age字段加上普通索引

2.1. 非唯一索引等值条件匹配场景:
  • 对于当前读,当条件数据存在时,除了加条件所在的「临建锁」还会加上往后第一个不匹条件的数据所在「间隙锁」
  • 对于当前读,当条件数据不存在时,条件所在的「临建锁 」会退化成「间隙锁」

【条件数据存在】:

  • 对age=15的行更新,加上了(10,15]的临建锁,往后第一个不匹配的数据是age=20,所以还会加上(15,20)间隙锁,所以锁的范围是(10,20)
    ·
    【条件数据不存在】:
  • 对age=13的行更新,加上了(10,15]的临建锁,但条件数据不存在,所以退化成(10,15)间隙锁
    ·
2.2. 非唯一索引条件范围匹配场景:
  • 对于当前读,锁的范围是条件范围所在的「临建锁」并集,起始范围可能不会退化成记录锁,并止于往后第一个不匹配范围的数据所在临建锁
  • 比如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才停止加锁
    ·

3. 条件没有索引对行级锁的影响:

  • 对于当前读,条件没有索引,会导致行级锁升级为表锁

name字段是没有索引的,不管数据存不存在,行级锁会升级为表锁
·

4. 小结:

【对于当前读,等值匹配条件数据存在时】

  • 唯一索引会将「临建锁 」会退化成「记录锁」
  • 非唯一索引「临建锁 」不会退化,并且还会加上往后第一个不匹条件的数据所在「间隙锁」
    ·

【对于当前读,等值匹配条件数据不存在时】

  • 唯一索引和非唯一索引「临建锁 」都会退化成「间隙锁」
    ·

【对于当前读,条件范围匹配】

  • 锁的范围是条件范围所在的「临建锁」并集,并止于往后第一个不匹配范围的数据所在临建锁
  • 唯一索引的起始范围可能会退化成记录锁,非唯一索引的起始范围不会退化成记录锁
    ·

【对于当前读,条件字段没有索引对行级锁的影响】

  • 会导致行级锁升级为表锁,使用时尤为注意

以上是基于mysql 8.0 + ,默认RR级别,索引对行级锁影响得实验结论,如有错误,欢迎指正

以上是关于MYSQL锁机制 - 锁的简述 | 索引对行级锁的影响的主要内容,如果未能解决你的问题,请参考以下文章

oracle行级锁和表级锁的区别?

MySQL中的行级锁,表级锁,页级锁

mysql的锁机制

MySQL中常见的锁

MySQL 间隙锁

MySQL中的行级锁表级锁页级锁