MySQL InnoDB锁类型

1)自增锁(AUTO-INC Locks),表级锁
用于AUTO_INCREMENT的自增主键,MySQL 8.0.3 之前系统变量innodb_autoinc_lock_mode默认值为1,simple insert语句可以提前确定插入条数,所以不用表锁只用互斥量就能拿到自增值,insert ... select查询插入不能提前确定插入条数所以需要拿到表锁之后每次一行的分配自增值。MySQL 8.0.3 之后系统变量innodb_autoinc_lock_mode默认值为2,完全不用拿到表锁。

2)意向锁(Intention Locks),表级锁
事务锁定了或即将锁定某个/某些数据行时, 首先要获取到父层次也就是表的意向锁。 LOCK_MODE分别是:IS或IX。
IS和IX互相可以共存,IS和IX和所有的行锁都能共存, 所以,意向锁只会阻塞表读写锁(例如:LOCK TABLES ... WRITE),不会阻塞其他任何东西。因为LOCK TABLES ... WRITE需要设置表的X锁,这会被意向锁IS或IX所阻塞。
使用意向锁,实现了“表锁是否冲突”的快速判断,否则当需要表的读写锁时,只能一行一行的检查是否已有行的读写锁。意向锁就是协调表的读写锁和行的读写锁(也就是不同粒度的锁)之间的关系的。

3)索引记录锁(Record Locks),行级锁
也就是所谓的行锁,锁定的是索引记录,行锁就是索引记录锁, LOCK_MODE分别是:S,REC_NOT_GAP或X,REC_NOT_GAP。

4)间隙锁(Gap Locks),行级锁,RR特有
gap锁加在指定索引值前面的间隙,通常是对第一个不满足条件的索引值加gap锁。例如 where i=11,下一个不等于11的 i 值为18,那给 i=18 加gap锁为(11,18)闭区间。
LOCK_MODE分别是:S,GAP或X,GAP,两种模式等价没有任何区别。gap lock可以共存, 事务T1持有某个间隙上的gap lock并不能阻止事务T2同时持有同一个间隙上的gap lock。

5)下一键锁(Next-Key Locks),行级锁,RR特有
是特殊的gap锁,是索引值record锁和gap锁的合体。如果索引值命中多个,则都加上nextKey。例如 i 值为 8-10-10-11-18,where i=10,那2个 i=10 的nextKey为 (8,10] 和 (10,10] 半开区间。
LOCK_MODE分别是:S或X。和gap不同的是,两种模式不等价,和记录锁类似,S之间可以共存,S和X,X和X之间不能共存,这点在重复键检查(duplicate-key)造成死锁时体现的很明显。

6)插入意向锁(Insert Intention Locks),行级锁,RR特有
是特殊的gap锁,加在插入值的下一个索引值上,例如 insert(i)values(13),下一个 i 值为18,那insert意向锁为 (13,18) 闭区间。
LOCK_MODE分别是:S,GAP,INSERT_INTENTION或X,GAP,INSERT_INTENTION,两种模式等价没有任何区别。insert意向锁之间可以共存,也不会阻塞gap锁和nextKey锁,但反过来会被gap锁和nextKey锁阻塞。

【总结】
gap,nextKey,insert意向锁 都是RR特有,insert意向锁存在的意义就是标记一个小区域,然后被前序gap锁和nextKey锁标记的大区域阻塞。gap锁存在的意义就是阻塞后序的insert意向锁。nextKey锁存在的意义就是阻塞后序的insert意向锁,以及nextKey内部之间的互斥。
至此,RR在 insert/update/delete/锁读 下的幻读就通过区域阻塞来解决了,但RR在快照读时会无视但允许别的事务的insert/update,所以快照读之后的insert/update还是会幻读到别的事务刚提交的insert/update。
RC常规情况都只对完全满足条件的索引值加record锁,但在外键约束检查(foreign-key constraint)以及重复键检查(duplicate-key)时会用上gap和nextKey锁,尤其是利用nextKey内部之间的互斥,这也是重复键检查(duplicate-key)容易发生死锁的原因。

【SQL会加什么锁】
RC的锁情况很简单,我们接下来讨论RR的锁使用,使用到锁的操作为 insert/update/delete/锁读:
1)如果是等值查询,那唯一索引上加record锁,辅助索引上加nextKey锁,并都在相应的聚集索引上加record锁,然后在第一个不满足条件的索引值上加gap锁。
2)如果是范围查询,不管唯一还是辅助索引都在每一个满足条件的索引值上加nextKey锁,并在相应的聚集索引上加record锁,然后在第一个不满足条件的索引值上加nextKey锁。
如果“无穷值”也在范围中,还会在“无穷值”加nextKey锁即(maxVal, infiniteVal)闭区间。例如 select where i>=18,那命中值 i=18 会有nexKey锁 (11, 18] 还有“无穷值”的nextKey锁 (18, infiniteVal]。
3)insert插入成功前会在(insertVal,nextVal)加 insert意向锁,插入成功时会在 insertVal 上加 record锁。
4)update的目标值同insert,update成功前会在 (targetVal, nextVal) 加insert意向锁,更新成功后会在 targetVal 上加 record锁。例如 update i=8 where i=11 那targetVal i=8 上会先后有 (8, 10) insert意向锁和 [8] record锁。
5)如果 insert/update/delete/锁读 没有用上索引,则对全表记录的主键索引上加nextKey锁,包括“无穷值”也加上nextKey锁。例如update set i=8 where name='xxx'。

你可能感兴趣的:(MySQL InnoDB锁类型)