Introduction to VPS and Web Technology Development

mysql锁

自由vps 数据库

一、 锁分类:

  1. 全局锁(整个数据库就处于只读状态了,这时对数据的增删查改操作,表结构的更改操作,都会被阻塞)
    1. 加锁方式:flush tables with read lock
    2. 释放锁:unlock tables
  2. 表级锁

    1. 意向锁(除了全表请求(例如,LOCK TABLES ... WRITE ) 之外,意图锁不会阻塞任何东西。意图锁的主要目的是表明有人正在锁定一行,或者要锁定表中的一行)
    2. 元数据锁 MDL(表结构锁, CRUD 操作时,加的是 MDL 读锁,做结构变更操作的时候,加的是 MDL 写锁

    3. AUTO-INC 锁(自增数锁)
    4. 表锁(整个表就处于只读状态了,这时对数据的增删查改操作,表结构的更改操作,都会被阻塞)

      1. 加锁方式:lock tables 表名 read;     lock tables 表名 wirte;
      2. 释放锁:unlock tables
  3. 行级锁
    1.  Record Lock,记录锁,也就是仅仅把一条记录锁上;
    2.  Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身;
    3.  Next-Key Lock 临键锁:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身,是行锁的基本单元。
    4. Insert Intention Locks 插入意向锁: 插入意向锁是一种特殊的间隙锁,该锁表示插入的意图,插入意向锁之间在行不冲突的情况下,不会相互阻塞,但与 Gap Lock 和Next-Key Lock 不兼容。

二、 加行锁场景:

  1. 只有在存在当前读场景的事务才会加锁
  2. 当前读和快照读

    InnoDB 给每一个事务生成一个唯一事务 ID 的方法称为生成快照,因此这种场景称为快照读

    但是对于更新数据不能使用快照读,因为更新数据时如果使用快照读会可能会覆盖其他事务的更改。另外查询时如果显式加锁也会采用当前读的方式。当前读就是读这个数据最新的提交数据。

    1. 当前读场景
      1. update ... (更新操作)
      2. delete ... (删除操作)
      3. insert ... (插入操作)
      4. select ... lock in share mode (共享读锁)
      5. select ... for update (写锁)
    2. 快照读的场景:
      1. 单纯的select操作 (不包括上面当前读的select ... lock in share mode,select ... for update)
      2. Read Committed隔离级别:每次select都生成一个快照读
      3. Read Repeatable隔离级别:开启事务后第一个select语句才是快照读的地方,而不是一开启事务就快照读

三、兼容性:

表级锁类型的兼容性

行级锁的兼容性


三、实际案例分析MySQL 是怎么加锁的?

数据背景:其中,id 是主键索引(唯一索引),b 是普通索引(非唯一索引),a 是普通的列。


案例一    唯一索引等值查询


会话1加锁变化过程如下:

加锁的基本单位是 next-key lock,因此会话1的加锁范围是(8, 16];
但是由于是用唯一索引进行等值查询,且查询的记录存在,所以 next-key lock 退化成记录锁,因此最终加锁的范围是 id = 16 这一行。
所以,会话 2 在修改 id=16 的记录时会被锁住,而会话 3 插入 id=9 的记录可以被正常执行。


会话1加锁变化过程如下:

加锁的基本单位是 next-key lock,因此主键索引 id 的加锁范围是(8, 16];
但是由于查询记录不存在,next-key lock 退化成间隙锁,因此最终加锁的范围是 (8,16)。
所以,会话 2 要往这个间隙里面插入 id=9 的记录会被锁住,但是会话 3 修改 id =16 是可以正常执行的,因为 id = 16 这条记录并没有加锁。

结论:

  • 当查询的记录是存在的,在用「唯一索引进行等值查询」时,next-key lock 会退化成「记录锁」
  • 当查询的记录是不存在的,在用「唯一索引进行等值查询」时,next-key lock 会退化成「间隙锁」

案例二    唯一索引范围查询


会话 1 加锁变化过程如下:

最开始要找的第一行是 id = 8,因此 next-key lock(4,8],但是由于 id 是唯一索引,且该记录是存在的,因此会退化成记录锁,也就是只会对 id = 8 这一行加锁;
由于是范围查找,就会继续往后找存在的记录,也就是会找到 id = 16 这一行停下来,然后加 next-key lock (8, 16],但由于 id = 16 不满足 id < 9,所以会退化成间隙锁,加锁范围变为 (8, 16)。
所以,会话 1 这时候主键索引的锁是记录锁 id=8 和间隙锁(8, 16)。

会话 2 由于往间隙锁里插入了 id = 9 的记录,所以会被锁住了,而 id = 8 是被加锁的,因此会话 3 的语句也会被阻塞。

由于 id = 16 并没有加锁,所以会话 4 是可以正常被执行。


案例三    非唯一索引等值查询


会话 1 加锁变化过程如下:

先会对普通索引 b 加上 next-key lock,范围是(4,8];
然后因为是非唯一索引,且查询的记录是存在的,所以还会加上间隙锁,规则是向下遍历到第一个不符合条件的值才能停止,因此间隙锁的范围是(8,16)。
所以,会话1的普通索引 b 上共有两个锁,分别是 next-key lock (4,8] 和间隙锁 (8,16) 。

那么,当会话 2 往间隙锁里插入 id = 9 的记录就会被锁住,而会话 3 和会话 4 是因为更改了 next-key lock 范围里的记录而被锁住的。

然后因为 b = 16 这条记录没有加锁,所以会话 5 是可以正常执行的。


会话 1 加锁变化过程如下:

先会对普通索引 b 加上 next-key lock,范围是(8,16];
但是由于查询的记录是不存在的,所以不会再额外加个间隙锁,但是 next-key lock 会退化为间隙锁,最终加锁范围是 (8,16)。
会话 2 因为往间隙锁里插入了 b = 9 的记录,所以会被锁住,而 b = 16 是没有被加锁的,因此会话 3 的语句可以正常执行。

结论:

  • 当查询的记录存在时,除了会加 next-key lock 外,还额外加间隙锁,也就是会加两把锁
  • 当查询的记录不存在时,只会加 next-key lock,然后会退化为间隙锁,也就是只会加一把锁。

案例四    非唯一索引范围查询


会话 1 加锁变化过程如下:



最开始要找的第一行是 b = 8,因此 next-key lock(4,8],但是由于 b 不是唯一索引,并不会退化成记录锁。
但是由于是范围查找,就会继续往后找存在的记录,也就是会找到 b = 16 这一行停下来,然后加 next-key lock (8, 16],因为是普通索引查询,所以并不会退化成间隙锁。
所以,会话 1 的普通索引 b 有两个 next-key lock,分别是 (4,8] 和(8, 16]。这样,你就明白为什么会话 2 、会话 3 、会话 4 的语句都会被锁住了。

总结:

非唯一索引和主键索引的范围查询的加锁规则不同之处在于:

  • 唯一索引在满足一些条件的时候,next-key lock 退化为间隙锁和记录锁。
  • 非唯一索引范围查询,next-key lock 不会退化为间隙锁和记录锁。



案例四    非唯一索引进阶案例

数据背景:其中,id 是主键索引(唯一索引),b 是普通索引(非唯一索引)。

会话1
会话2
会话3
会话4
会话5
会话6
会话7
会话8
会话9
SELECT * FROM z WHERE b = 6 FOR UPDATE;









INSERT INTO z VALUES (2, 4); /*success*/

				

				








INSERT INTO z VALUES (2, 8);
/*blocked*/









INSERT INTO z VALUES (4, 4); /*blocked*/









INSERT INTO z VALUES (4, 8); /*blocked*/









INSERT INTO z VALUES (8, 4); /*blocked*/









INSERT INTO z 
VALUES (8, 8);
 /*success*/









INSERT INTO z VALUES (0, 4); /*blocked*/









INSERT INTO z 
VALUES (-1, 4); 
/*success*/


会话1加锁过程

  1. 在索引 b 上的等值查询,给索引 b 加上了 next-key lock (4, 6];索引向右遍历,会再加上间隙锁 (6,8);
  2. 所以索引 b 上的 next-key lock 的范围是(b=4,id=3)到(b=6,id=5)这个左开右闭区间和(b=6,id=5)到(b=8,id=7)这个开区间。
  3. for update 会给 b = 6 这一行加上行锁;因此 (b=6,id=5) 这一行上有行锁

其余会话分析:


  • 因为索引是有序的,此时,由于记录(b=4,id=3)的存在,(b=4,id=2)不在锁的范围内,可以插入,但(b=4,id=4)在锁的范围内,所以插入时需要等待锁释放,被 blocked  对于其他(id,b)的值(2,8),(4,8),(8,4),(8,8)也是同样的道理;






使用chatGPT寻求答案
标签: 暂无标签

免责声明:

本站提供的资源,都来自网络,版权争议与本站无关,所有内容及软件的文章仅限用于学习和研究目的。不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负,我们不保证内容的长久可用性,通过使用本站内容随之而来的风险与本站无关,您必须在下载后的24个小时之内,从您的电脑/手机中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。侵删请致信E-mail:master@freevpsweb.com

同类推荐
评论列表