博客 浅谈 MySQL 的七种锁

浅谈 MySQL 的七种锁

   小美   发表于 2023-01-05 16:28  503  0

时常会有开发的同学突然紧张兮兮地找我,

然后丢给我一个代码层面的

CannotAcquireLockException 的报错,

一脸无辜地问我是不是自己搞出了一个死锁。

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/361433a4ed441b6b5d17bd2a0b74e1b0..jpg

 

好像大家看到 LOCK 的字眼

总会第一时间想到死锁而忽略了锁,

难道我们锁没有面子的嘛,

我们锁的大家族可足足有七种呢?

那么到底什么是锁,有哪些锁,

请听我娓娓道来。。

01 共享锁(S 锁)和排它锁(X 锁)

事务拿到某一行记录的共享 S 锁,才可以读取这一行,并阻止别的事物对其添加 X 锁

事务拿到某一行记录的排它 X 锁,才可以修改或者删除这一行

共享锁的目的是提高读读并发

排他锁的目的是为了保证数据的一致性

02 意向锁

1)意向共享锁

预示事务有意向对表中的某些行加共享 S 锁

2)意向排他锁

预示着事务有意向对表中的某些行加排他 X 锁

3) IS、S、IX、X 锁之间的兼容性比较

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/3b100a22dd8eff78ea5b8ea275e9fb75..jpg

 

4)意向锁的意义在哪里?

1.IX,IS 是表级锁,不会和行级的 X,S 锁发生冲突。只会和表级的 X,S 发生冲突

2. 意向锁是在添加行锁之前添加。

3. 如果没有意向锁,当向一个表添加表级 X 锁时,就需要遍历整张表来判断是否存行锁,以免发生冲突

4. 如果有了意向锁,只需要判断该意向锁与表级锁是否兼容即可。

03 插入意向锁(insert intention looks)

插入意向锁是间隙锁的一种,针对 insert 操作产生。

目的是提高插入并发。

多个事物,在同一个索引,同一个范围区间进行插入记录的时候,如果 插入的位置不冲突,不会阻塞彼此。

示例:

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/7e54cc20f33d6477cf21bada6a136a4b..jpg

 

由于事物一和事物二都是对表的同一索引范围进行 insert, 使用的插入意向锁,由于插入的记录并不冲突,所以并不会阻塞事物二。如果事物二插入的记录与事物一冲突,会被 X 锁阻塞。

04 记录锁

对单条索引记录进行加锁,锁住的是索引记录而非记录本身,即使表中没有任何索引,MySQL 会自动创建一个隐式的 row_id 作为聚集索引来进行加锁。

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/67596c87daab5758943acf22afd98acd..jpg

 

05 间隙锁(gap 锁)

封锁记录中的间隔,防止间隔中被其他事务插入。

间隙锁主要出现在 RR 隔离级别,避免出现幻读。

MVCC(多版本并发)

1.MVCC 的作用

避免脏读、写不阻塞读、实现可重复读、多版本控制

2. 在 MVCC 下,读操作可以分为两种:快照读、当前读

1)快照读

select * from tbl_name where ...

2)当前读

select * from tbl_name where ... for update;

update

delete

insert

3)为什么 delete/update 也是一种当前读?(如一个 update 操作)

a. 在进行 update 的时候,MySQL 会根据 where 条件得到过滤出来的第一条记录,并进行加锁(currenet read)

b. 对该条记录进行 update

c. 再次读取下一条记录,直到没有满足条件的记录为止

d.delete 原理与之类似

4)为什么 insert 也是一种当前读?

insert 操作可能会触发 Unique Key 的冲突检查,也会进行一个当前读。

隔离级别

1.Read Uncommitted

可以读取到未提交的事物。

2.Rrad Committed(RC)

针对当前读,RC 隔离级别保证对读取到的记录加锁 (记录锁),存在幻读现象。

3.Repeatable Read (RR)

针对当前读,RR 隔离级别保证对读取到的记录加锁 (记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁),不存在幻读现象。

4.Serializable

所有的读操作均为当前读,读加读锁 (S 锁),写加写锁 (X 锁)。

读写冲突,并发行很差。

几种触发间隙锁的情况

1.id 非唯一索引 + RR

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/ab787d4d89ab00d68e4e47573deaa232..jpg

 

SQL:delete from t1 where id = 10;

加锁流程如下:

a. 通过 id 索引定位到第一条满足查询条件的记录,加记录上的 X 锁,加 GAP 上的 GAP 锁,

b. 然后加主键聚簇索引上的记录 X 锁,然后返回;

c. 然后读取下一条,重复进行。

d. 直至进行到第一条不满足条件的记录 [11,f],此时,不需要加记录 X 锁,但是仍旧需要加 GAP 锁,最后返回结束。

2.id 无索引 + RR

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/327a9fd8212b30f542e651dba72e2948..jpg

 

SQL:delete from t1 where id = 10;

a. 由于 id 字段无索引,进行全表扫描的当前读,

b. 聚簇索引上的所有记录,都被加上了 X 锁。其次,聚簇索引每条记录间的间隙都被加上了 GAP 锁。

3. 针对 id 无索引 + RR MySQL 性能上做的一些优化

semi-consistent read

semi-consistent read 开启的情况下,对于不满足查询条件的记录,MySQL 会提前放锁。

针对上面的这个用例,就是除了记录 [d,10],[g,10] 之外,所有的记录锁都会被释放,同时不加 GAP 锁。

4.semi-consistent read 如何触发?

1)隔离级别是 read committed;

2)隔离级别是 Repeatable Read,同时设置了 innodb_locks_unsafe_for_binlog 参数。

示例一

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/8d3bdade76ebfbc88144d7df40e123b4..jpg

 

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/1979ff798227508e2a203a9c1c26ab4a..jpg

 

示例二

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/b5bbfd04260b35b42c9eab97ddca504c..jpg

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/b211bcad424805e4730b13cde43fe610..jpg

 

示例三

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/559cd9e1657ae910d13bcc052b72e2d5..jpg

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/bbd3aa6c89f524e5e12fe125d835b1cd..jpg

 

06 临键锁 (Next-Key Locks)

临键锁是记录锁和间隙锁的组合,既锁住了记录也锁住了范围。

临键锁的主要目的,也是为了避免幻读。

如果把事务的隔离级别降级为 RC,临键锁就也会失效。

通常情况下,InnoDB 在搜索或扫描索引的行锁机制中使用 “临键锁 (next-key locking)” 算法来锁定某索引记录及其前部的间隙 (gap),以阻塞其它用户紧跟在该索引记录之前插入其它索引记录。

innodb_locks_unsafe_for_binlog 默认为 OFF,意为禁止使用非安全锁,也即启用间隙锁功能。将其设定为 ON 表示禁止锁定索引记录前的间隙,也即禁用间隙锁,InnoDB 仅使用索引记录锁 (index-record lock) 进行索引搜索或扫描,不过,这并不禁止 InnoDB 在执行外键约束检查或重复键检查时使用间隙锁。

innodb_locks_unsafe_for_binlog 的效果:

(1)对 UPDATE 或 DELETE 语句来说,InnoDB 仅锁定需要更新或删除的行,对不能够被 WHERE 条件匹配的行施加的锁会在条件检查后予以释放。这可以有效地降低死锁出现的概率;

(2)执行 UPDATE 语句时,如果某行已经被其它语句锁定,InnoDB 会启动一个 “半一致性 (semi-consistent)” 读操作从 MySQL 最近一次提交版本中获得此行,并以之判定其是否能够并当前 UPDATE 的 WHERE 条件所匹配。如果能够匹配,MySQL 会再次对其进行锁定,而如果仍有其它锁存在,则需要先等待它们退出。

(3)innodb_locks_unsafe_for_binlog 可能会造成幻读

示例一

innodb_locks_unsafe_for_binlog=off 的情况下:

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/ad9081c66069805d3e815ed943ee178b..jpg

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/e27167a9282a34e26b02d03f1a39ac1f..jpg

 

示例二

innodb_locks_unsafe_for_binlog=on 的情况下:

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/a24c74a2fadd5c28261dd210f0b14f0e..jpg

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/a5f96bd4fc193fe8c6611edd50ac300f..jpg

 

查看 binlog 日志:

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/a7be732c9c3cec7abe79f12f385dfca6..jpg

 

因此,当 innodb_locks_unsafe_for_binlog=on 的情况下,会让你容易造成数据的不一致。

07 自增长锁

自增长锁是一种表级锁,专门针对 auto_increment 类型的列。

自增长列锁各模式分析:

innodb_autoinc_lock_mode:自增长长锁模式

0:

不管是 insert into values (simple insert) 还是 insert into select (bulk insert),都是:持有锁、读取 / 修改、执行 SQL、释放,不需要等到事务提交就释放锁,但是需要 SQL 执行完成,并且不能保证连续。

持有 latch ---> 读取和修改 auto 锁 ---> 执行 insert ---> 释放

注意:不需要等待 insert 所在的事务是否提交

缺点:可能出现数字不连续

持有时间相对过长:SQL 执行完毕,不需要事务提交

1:

默认值,对于回滚是不能保证自增长列连续的。

对于 simple insert (insert into values):持有锁、读取、释放、执行 SQL, 最快,不需要执行完 SQL 就释放,不需要等待 insert 执行完毕就可以释放锁。

对于 bulk insert (insert into select):持有锁、读取、执行 SQL、释放,需要执行完 SQL 才释放。(对于批量 insert 来说等同于 0)

优点:

对于 simple insert 来说,性能比 0 好些,对于批量来说,性能等同于 0

缺点:

数字不连续

对于批量来说持有锁的时间相对过长

2:

经常改为 2,主要是为了唯一,不是为了连续,在批量 insert 时或者批量 insert 并发的时候用

优点:速度最快

缺点:只能保证唯一,不能保证递增和连续。持有、读取和修改、释放、执行 SQL

建议修改成 2,对于批量的 insert 可以提升性能

示例

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/43b9d4f71ef4ee2c009087e41fed3a8e..jpg

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user6/article/d112b16303852da5a55df9ae422546e5..jpg

由于 innodb_autoinc_lock_mode=1,所以事物一并不会阻塞事物二的 simple insert,保证了 id 字段的唯一性。

想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:https://www.dtstack.com/?src=bbs

同时,欢迎对大数据开源项目有兴趣的同学加入「袋鼠云开源框架钉钉技术群」,交流最新开源技术信息,群号码:30537511,项目地址:https://github.com/DTStack

0条评论
社区公告
  • 大数据领域最专业的产品&技术交流社区,专注于探讨与分享大数据领域有趣又火热的信息,专业又专注的数据人园地

最新活动更多
微信扫码获取数字化转型资料
钉钉扫码加入技术交流群