作 者
宋丹琪(花名:三思)
袋鼠云云服务部DBA团队
数据库工程师
时常会有开发的同学突然紧张兮兮地找我,
然后丢给我一个代码层面的
CannotAcquireLockException的报错,
一脸无辜地问我是不是自己搞出了一个死锁。
好像大家看到LOCK的字眼
总会第一时间想到死锁而忽略了锁,
难道我们锁没有面子的嘛,
我们锁的大家族可足足有七种呢?
那么到底什么是锁,有哪些锁,
请听我娓娓道来。。
01
共享锁(S锁)和排它锁(X锁)
事务拿到某一行记录的共享S锁,才可以读取这一行,并阻止别的事物对其添加X锁
事务拿到某一行记录的排它X锁,才可以修改或者删除这一行
共享锁的目的是提高读读并发
排他锁的目的是为了保证数据的一致性
02
意向锁
1)意向共享锁
预示事务有意向对表中的某些行加共享S锁
2)意向排他锁
预示着事务有意向对表中的某些行加排他X锁
3) IS、S、IX、X锁之间的兼容性比较
兼容性 | IS | IX | S | X |
---|---|---|---|---|
IS | 兼容 | 兼容 | 互斥 | 互斥 |
IX | 兼容 | 兼容 | 互斥 | 互斥 |
S | 兼容 | 互斥 | 兼容 | 互斥 |
X | 互斥 | 互斥 | 互斥 | 互斥 |
4)意向锁的意义在哪里?
1.IX,IS是表级锁,不会和行级的X,S锁发生冲突。只会和表级的X,S发生冲突
2.意向锁是在添加行锁之前添加。
3.如果没有意向锁,当向一个表添加表级X锁时,就需要遍历整张表来判断是否存行锁,以免发生冲突
4.如果有了意向锁,只需要判断该意向锁与表级锁是否兼容即可。
03
插入意向锁(insert intention looks)
插入意向锁是间隙锁的一种,针对insert操作产生。
目的是提高插入并发。
多个事物,在同一个索引,同一个范围区间进行插入记录的时候,如果 插入的位置不冲突,不会阻塞彼此。
示例:
由于事物一和事物二都是对表的同一索引范围进行insert,使用的插入意向锁,由于插入的记录并不冲突,所以并不会阻塞事物二。如果事物二插入的记录与事物一冲突,会被X锁阻塞。
04
记录锁
对单条索引记录进行加锁,锁住的是索引记录而非记录本身,即使表中没有任何索引,MySQL会自动创建一个隐式的row_id作为聚集索引来进行加锁。
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锁)。
读写冲突,并发行很差。
SQL:delete from t1 where id = 10;
加锁流程如下:
a.通过id索引定位到第一条满足查询条件的记录,加记录上的X锁,加GAP上的GAP锁,
b.然后加主键聚簇索引上的记录X锁,然后返回;
c.然后读取下一条,重复进行。
d.直至进行到第一条不满足条件的记录[11,f],此时,不需要加记录X锁,但是仍旧需要加GAP锁,最后返回结束。
2.id无索引+RR
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 参数。
示例一
示例二
示例三
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的情况下:
示例二
innodb_locks_unsafe_for_binlog=on的情况下:
查看binlog日志:
因此,当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可以提升性能
示例
由于innodb_autoinc_lock_mode=1,所以事物一并不会阻塞事物二的simple insert,保证了id字段的唯一性。
参考引用:
何登成的技术博客——《MySQL 加锁处理分析》
微信公众号:架构师之路
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/31556433/viewspace-2219082/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/31556433/viewspace-2219082/