https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
共享锁和排他锁
InnoDB 实现了标准的行级锁,有两种类型:shared locks、exclusive locks,有些地方会简写为 S、X。
共享锁允许持有该锁的事务读取某一行;排他锁允许持有该锁的事务更新或删除某一行。
如果事务 T1 持有某一行 r 的 S 锁,来自事务 T2 对于锁的请求,不同情况如下:
- 事务 T2 请求 S 锁立即被授予。如此,T1 和 T2 同时持有对 r 的 S 锁;事务 T2 请求 X 锁无法被立即授予。
如果事务 T1 持有某一行 r 的 X 锁,来自事务 T2 对于任意类型的锁都无法立即授予。事务 T2 必须等待 T1 释放在行 r 上的锁定。
意向锁MySQL 8.0 观测行锁 data_locks data_lock_waits
InnoDB 支持多粒度锁,行锁和表锁可以共存。例如,LOCK TABLES ... WRITE 语句在指定表上设置排他锁(X 锁)。
关于 LOCK TABLES 和 UNLOCK TABLES 的语句可以参考 13.3.6 LOCK TABLES and UNLOCK TABLES Statements
为了确保多粒度锁的可行,InnoDB 采用意向锁。意向锁是表级锁,表示事务在稍后需要给表的某一行施加 S 或者 X 锁。有两种意向锁:
意向共享锁(IS)表示事务意图在某一行上设置 S 锁;意图排他锁(IX)表示事务意图在某一行上设置 X 锁
例如,SELECt ... FOR SHARE 可以设置 IS 锁,SELECT ... FOR UPDATE 可以设置 IX 锁。
意向锁的协议如下:
在事务可以获取 S 锁之前,它必须先获取 IS 锁或者更强的锁。在事务可以获取 X 锁之前,它必须先获取 IX 锁或者更强的锁。
表级锁的兼容性如下:
| X | IX | S | IS |
|---|---|---|---|
| X | Conflict | Conflict | Conflict |
| IX | Conflict | Compatible | Conflict |
| S | Conflict | Conflict | Compatible |
| IS | Conflict | Compatible | Compatible |
某事务请求锁,如果该锁与现有锁兼容,则可以授予;如果冲突则拒绝。事务等待,直到释放锁。如果锁的请求与现有锁冲突,并且因为死锁的原因无法授予,这就发生了错误。
意向锁不会阻止任何请求,除了全表请求(例如,LOCK TABLES … WRITE)。意向锁的目的在于,表明某个事务正在锁定某一行,或者即将锁定某一行。
记录锁(Record Locks)记录锁是锁在索引记录的锁。例如:SELECT c1 FROM t WHERe c1 = 10 FOR UPDATe; 防止其他事务 insert、update、delete 满足 t.c1 = 10 的行。
记录锁锁的并不是行记录,而是索引。
记录锁始终锁定的是索引记录,即使是没有索引定义的表。对于这种情况,InnoDB 创建隐藏的聚簇索引,并让这个索引用于记录锁。
间隙锁记录锁属于行锁,而且是 X 锁
间隙锁,可以锁在索引之间,或者第一个索引记录之前,或者最后一个索引记录之后。
尽管叫间隙锁,第一个索引之前,最后一个索引之后也可以产生间隙锁。
例如,SELECT c1 FROM t BETWEEN 10 AND 20 FOR UPDATe; 如果其他事务意图插入 t.c1=15 的值,将会被阻止。无论是否列中是否存在此类值,因为范围内的值都会被锁定。
间隙可能跨越 1 个索引值,多个索引值,甚至也可能是空的。
间隙锁是性能与并发之间权衡的一部分,并且仅仅用于某些事务隔离级别。
对于那些使用唯一索引去搜索唯一的行的语句,是不需要间隙锁的。例如,如果 id 列具有唯一索引,那么下面的语句仅仅使用了 id = 100 的记录锁,并且其他会话是否在前面的间隙中插入行,都无关紧要。
SELECT * FROM child WHERe id = 100;
如果 id 不是索引列,或者具有非唯一索引,则该语句锁定前面的间隙。
需要注意的是,相互冲突的锁可以一个间隙之中被不同事务持有。例如,当事务 B 在一个间隙上持有排他间隙锁(gap X-lock)时,事务 A 可以持有一个共享间隙锁(gap S-lock)。
InnoDB 中的间隙锁是纯粹禁止,这意味着它们唯一的目的就是防止其他事务插入到间隙中。间隙锁可以共存。一个事务持有的间隙锁不会阻止另一个事务在相同的间隙上持有间隙锁。共享间隙锁和排他间隙锁之间没有区别。它们不会互相冲突,它们具有相同的效果。
临键锁临键锁是记录锁与间隙锁的组合,其实就是某一条索引的记录锁以及这条索引记录之前的间隙锁。
InnoDB 以这样的方式执行行级锁定:当 InnoDB 扫描表索引时,它会在它遇到的索引记录上设置 S 锁或者 X 锁。因此,行级锁实际上时索引记录锁。索引记录上的临键锁也会影响索引记录之前的间隙。也就是说,临键锁是某一个索引记录锁 + 这条索引记录之前的间隙锁。如果一个会话持有记录 R 的索引 S 锁或者 X 锁,那么以索引的顺序排序,其他会话无法在记录 R 之前的间隙中立即插入一个新的索引记录。
假设索引值包含 10、11、13、20。该索引可能的临键锁包含以下间隔:
(negative infinity, 10] (10, 11] (11, 13] (13, 20] (20, positive infinity)



