博客 浅谈MySQL的七种锁

浅谈MySQL的七种锁

   小美   发表于 2021-11-25 17:23  649  0

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

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

CannotAcquireLockException的报错,

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


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


好像大家看到LOCK的字眼

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

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

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

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

请听我娓娓道来。。


01

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


事务拿到某一行记录的共享S锁,才可以读取这一行,并阻止别的事物对其添加X锁
事务拿到某一行记录的排它X锁,才可以修改或者删除这一行
共享锁的目的是提高读读并发
排他锁的目的是为了保证数据的一致性


02

意向锁


1)意向共享锁

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


2)意向排他锁

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


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

兼容性ISIXSX
IS兼容兼容互斥互斥
IX兼容兼容互斥互斥
S兼容互斥兼容互斥
X互斥互斥互斥互斥


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_user1/article/d7a0cc85fdeac7806544247a51f0badf..jpg


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


04

记录锁


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


http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/ede2c55296afdf7344694a172ae864bd..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_user1/article/7d3f3a4d2a1a9a2a2a85154125b7f0e5..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_user1/article/3f336bde537425f7d4a742a648e61a91..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_user1/article/7bfb3f22e5acda7db0d3794db7e9d42b..jpg

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


示例二

https://mmbiz.qpic.cn/mmbiz_jpg/Xxm5O5TNdibjpmFR2TeLAjqu4iccntHeicIA3NicHbIW3Nb14YJaDOHpIVmI2ZZvKqFIBPNtKdFrZnV2MN6WK7vSIQ/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/0103fd8ebfb1885f29de39b2e7419535..jpg


示例三

https://mmbiz.qpic.cn/mmbiz_jpg/Xxm5O5TNdibiaegcHMD6sLVVib2c80A7IjGgjuRty8aQF8PTs71kGxhkwpgzHlc6QxFxlu63bP5uWCbGmodJ2xnKA/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/005aba48ce402473ccace11ec0a90efd..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_user1/article/637ee4e0b1a0d37c88903e1ddd5352f1..jpg

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/4ee55204109260708a36c2d2261edece..jpg


示例二


innodb_locks_unsafe_for_binlog=on的情况下:


http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/6679e80a437ff5854bae3a3a58b1990d..jpg

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

   

查看binlog日志:


http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/72b93a44270e4349ad00d9dad5b52388..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_user1/article/3c0b030d087b63e794fa7991ef175e6a..jpg

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/6ab8a650ffbd1b040e02a7a7e8b86253..jpg


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

参考引用:

何登成的技术博客——《MySQL 加锁处理分析》
微信公众号:架构师之路

http://dtstack-static.oss-cn-hangzhou.aliyuncs.com/2021bbs/files_user1/article/9d4f349fcd02654233aaed37e8f555b8..jpg



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

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