锁是数据库系统系统区别于文件系统的一个关键特性。
参考:https://blog.ouyangsihai.cn/mysql-de-you-yi-shen-qi-suo.html
一、lock和 latch
1. latch
latch在MySQL中是用来保证并发多线程操作操作临界资源的锁,
锁定的对象线程
,是和咱们使用的Java等传统语言中的锁意义相近,而且没有死锁检测的机制
。
2. lock
lock是MySQL中在事务中使用的锁,
锁定的对象是事务
,来锁定数据库中表、页、行
;通常只有在事务commit或者rollback后进行释放。lock是有死锁机制的
,当出现死锁时,lock有死锁机制来解决死锁问题:超时时间(参数innodb_lock_wait_timeout)、wait-for graph。
我们通常讲的MySQL的“锁”,一般就是说的lock。以下就是InnoDB中“锁”的大分类:
二、lock的种类
MySQL Lock大体上可以分为:表锁、行锁、意向锁
三种。
1.共享/排他锁
行锁分为
:S Lock和X Lock。S Lock :读锁;X Lock:写锁
。
两锁之间的兼容性如下。
X S
X 不兼容 不兼容
S 不兼容 兼容
简单总结为:读锁可以读、不可写;写锁不可读、也不可写。
2.意向锁
可以参考:https://juejin.im/post/5b85124f5188253010326360
意向锁是一种不与行级锁冲突表级锁,这一点非常重要。意向锁分为两种:
- 意向共享锁(intention shared lock, IS):事务有意向对表中的某些行加共享锁(S锁)
事务要获取某些行的 S 锁,必须先获得表的 IS 锁。 SELECT column FROM table ... LOCK IN SHARE MODE;
- 意向排他锁(intention exclusive lock, IX):事务有意向对表中的某些行加排他锁(X锁)
事务要获取某些行的 X 锁,必须先获得表的 IX 锁。 SELECT column FROM table ... FOR UPDATE;
即:
意向锁是有数据引擎自己维护的
,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁
。InnoDB支持多粒度的锁,即:允许表锁和行锁同时存在。
但是,假如表锁覆盖了行锁的数据,所以表锁和行锁也会产生冲突。如:trx1 BEGI trx1 给 T1 加X锁修改数据。 trx2 BEGIN trx2 给 T1 加表锁修改表结构
表锁和行锁之间就产生了冲突,为了解决这种表锁和行锁共存的问题,就产生了意向锁这个东西。
意向锁:从字面意思也很好理解,就是提前表明一个“意向”
。意向锁
仅仅是表明意向
,它其实非常弱,意向锁之间可以相互并行,并不是排斥的
。意向锁之间的兼容性问题:IS IX IS 兼容 兼容 IX 兼容 兼容
于是,上述现象就变为了:
trx1 BEGIN trx1 给 T1 先加IX ,然后在某一行记录加X锁。 trx2 BEGIN trx2 给 T1 加表锁X(事务被阻塞,等待加锁成功) trx2 修改表结构
意向锁存在的意义是什么呢?
”没有意向锁,行锁和表锁照样可以共存,试问如何共存?” “查看表中某一行存在X锁” “如何查看呢?” 唯有全表扫描…
意向锁的存在就是
解决了“全表扫描”的性能问题
,所以,意向锁一定是“表级”锁,告诉整张表XXX行存在X锁
。此时假如进行表操作就会被阻塞。- 总结
InnoDB 支持多粒度锁,特定场景下,行级锁可以与表级锁共存。
意向锁之间互不排斥,但除了 IS 与 S 兼容外
,意向锁会与 共享锁 /排他锁 互斥。
IX,IS是表级锁,不会和行级的X,S锁发生冲突
。只会和表级的X,S发生冲突
。
意向锁在保证并发性的前提下,实现了行锁和表锁共存且满足事务隔离性的要求。3.主键自增锁
自增锁(auto-inc Locks)是一种特殊的表级锁
,专门针对事务插入AUTO_INCREMENT类型的列,往往就是主键列。可以保证主键的值自增是“原子操作”,不会出现一致性、唯一性问题。4.行锁的具体分类
InnoDB存储引擎有以上3种行锁算法。以上3种,都是实现在索引上的。①记录锁(Record Lock)
记录锁(Record Lock)总是会去锁住索引记录。
假如没有任何一个索引,那么InnoDB会锁住隐形创建的那个主键。注意:这里锁的是索引,不一定只是主键索引哦,还可能是二级普通索引。
②间隙锁(Gap Lock)
顾名思义,它会封锁索引记录中的“缝隙”
,防止其他事务在“缝隙”中插入数据。
它锁定的是一个不包含索引本身的范围。例如以下索引数据:
间隙锁(Gap Lock)
可以锁的将是以下范围
具体的范围还要根据查询条件不同而定。
间隙锁开启的事务隔离级别是 Repeatable Read
,如果把数据库事务级别降为Read Committed(默认是 Repeatable Read),间隙锁则会自动失效。
③ 临键锁(Next-Key Lock)
Next-Key Lock可以说是记录锁(Record Lock)和间隙锁(Gap Lock)的组合
,既封锁了”缝隙”,又封锁了索引本身
。还是上面的索引数据:
临键锁(Next-Key Lock)锁住的范围将是:
Next-key Lock在索引具有唯一性的时候,例如主键索引的数据,将会降级为记录锁(Read Lock),以增加并发性。
例如:T1 T2 BEGIN; | select * From T | where id = 5 for update | --------------------------------------------------------------- | BEGIN; | Insert into t (4, xx); --------------------------------------------------------------- | COMMIT | ---------------------------------------------------------------- COMMIT | | | -----------------------------------------------------------------
以上情况,就会把Next-key Lock降级为记录锁(Read Lock)