Mysql锁(翻译)

时间:2023-03-08 23:42:44
Mysql锁(翻译)

内容主要是对mysql文档的翻译。

1. shared(s) 共享锁
2. exclusive(x) 排它锁

innodb的s锁和x锁是行级锁。
事务T1获得s锁,事务T2仍然可以获得s锁。
事务T1获得x锁,事务T2只有在T1释放该锁后,才能获得x锁。
共享锁的作用是,就像在说“我现在要读这条数据,你们不要改它”。
排它锁就是:“我现在要改这条数据,所以你们不要改,也不要读”

3. Intention Locks
意向锁
innodb支持允许行级锁和表级锁共存的多粒度锁。为了使多粒度锁能实用,使用了一种被称为意向锁的附加类型的锁。意向锁是innodb中的表级锁,它表明一个事务为了表中的某一行后续需要哪种类型的锁(共享或排他)。在innodb中使用了2种意向锁(假定事务T请求了表 t 某种类型的锁):
intention shared(IS): 事务T想要对表 t 中的某些行加s锁。
intention exclusive(IX):事务T想要对表 t 中的某些行加x锁。

例如:select ... lock in share mode 加IS锁,而 select ... for update 加IX锁。
意向锁的规则如下:
在一个事务获取表t中某行的s锁之前,它必须先获取一个表t的IS锁或者更强的锁。
在一个事务获取表t中某行的x锁之前,它必须先获取一个表t的IX锁。

Mysql锁(翻译)

一个锁会赋予给请求事务,如果请求的锁与事务现有的锁兼容。事务会等待直到现有的冲突锁被释放。如果一个请求的锁和现有的锁冲突了,导致无法获取锁,这会导致死锁,出错。
因此,意向锁不会阻塞任何事,除了对整张表的请求(lock table ... write)。IX和IS锁的主要作用是告诉别人,有人正在锁住某行记录,或者将要锁住表中的某行。

4. Record Locks 记录锁
一个记录锁是一条索引记录的锁。例如, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 阻止其他事务插入,更新或删除t.c1值等于10的行。
记录锁通常锁住索引记录,即使一张表并没有定义索引。针对这样的情况,innodb创建一种隐藏的聚集索引,并且使用这种索引来锁住记录。看Section 14.8.9, “Clustered and Secondary Indexes”。

5. Gap Locks 间隙锁
间隙锁是索引记录的间隙上的锁,或者是第一行之前的间隙,或者最后一行之后的间隙。例如,
select c1 from t where c1 between 10 and 20 for update; 阻止其他事务插入t.c1等于15的行,不管表中是否存在t.c1等于15,因为在该范围内,所有现有值之间的间隙都被锁定。

一个间隙可能包含0个、一个、多个索引值。

间隙锁是一种性能和并发之间的折中,使用在某些事务隔离级别上。
对于通过使用唯一索引查询某一行并锁住该行的语句,并不需要间隙锁。(查询条件中只包含“多列唯一索引”的部分列,这种情况仍然会产生间隙锁)。例如,id列有唯一索引,下面的语句只对id等于100的行用了索引记录锁,不会影响其他事务在这个间隙内插入行。
select * from child where id = 100;

如果id列没有索引,或者有非唯一索引,上面的语句会锁住间隙。
这里值得注意的是:不同的事务可以在一个间隙上持有冲突锁。例如,事务A可以持有一个共享间隙锁(gap S-lock),同时事务B在相同的间隙上持有排他间隙锁(gap X-lock)。允许冲突的间隙锁的原因是:如果一条记录根据索引被清除了,不同事务在这条记录上的间隙锁必须合并。

innodb中的间隙锁是“单纯禁止的”,意味着他门只堵塞其他事务往间隙内插入数据。他门不会阻止其他事务从相同的间隙获取锁。因此,gap X-lock和gap S-lock的效果是一样的。

间隙锁可以显式地被关闭。这会发生,当你设置事务隔离级别为读已提交或者使innodb_locks_unsafe_for_binlog (现在已经废弃)系统变量生效。在这些情况下,间隙锁对于查询,索引扫描没有启用,只在外键约束检查和重复键检查时起作用。

设置事务隔离级别为读已提交,或者使innodb_locks_unsafe_for_binlog系统变量生效也有其他作用。当MySQL计算where表达式后,非匹配行上的记录锁会被释放。对于update语句,innodb做了一次“半一致性读”,所以它返回最新的提交版本给mysql,所以mysql能决定行是否匹配update的where条件。

6. Next-Key Locks
Next-Key 锁是两种锁的结合:索引记录上的记录锁和在索引记录前的间隙上的间隙锁。

innodb 采用行级锁,导致当它查询或扫描一个表索引时,它会在遇到的的索引记录上加共享或排他锁。因此,
行级锁实际上是索引记录锁。一条索引记录上的next-key锁也会影响索引记录前的“间隙”。那就是,next-key锁是索引记录锁
加上索引记录前的间隙的间隙锁。如果一个会话拥有索引记录R上的共享锁或排他锁,另一个会话不能立刻在R索引顺序前面的间隙中插入新的索引记录。
假定一个索引包含值10, 11, 13, 和 20。这个索引可能的next-key锁覆盖下面的间隔,一个圆括号表示开区间,方括号表示闭区间:
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
最后一个间隔,next-key锁锁住索引中最大值和大于索引中任何值的“上界”的间隙。这个上界不是真实的索引记录,所以,效果上,这个next-key锁只锁住了包含最大索引值的间隙。

默认地,innodb 运行在可重复读事务隔离级别,设置 innodb_locks_unsafe_for_binlog 系统变量为disabled。在这种情况下,innodb 在查询和索引扫描时使用next-key锁,以阻止幻影行。

7. Insert Intention Locks
插入意向锁

插入意向锁是一种间隙锁,插入操作加的在插入行前的间隙锁。锁以这种方式提示插入的意向,导致不同事务插入相同索引间隙不需要等待其他人,如果他们不是插入间隙中的相同位置。
假定,存在值4和7的索引记录,分别有两个事务试图插入5和6,每个事务在获得已插入行的排他锁之前,分别用插入意向锁锁住4和7之间的间隙,但是不会阻塞其他人因为插入的行是不冲突的。

下面的例子展示一个事务在获得插入记录的排他锁之前,会获取插入意向锁。示例中涉及2个客户端,A和B。
A创建一张包含2条索引记录(90和102)的表,然后开始一个事务,用大于100的id在索引记录上加排他锁。排他锁包含记录102钱的间隙锁。
mysql> CREATE TABLE child (id int(11) NOT NULL, PRIMARY KEY(id)) ENGINE=InnoDB;
mysql> INSERT INTO child (id) values (90),(102);

mysql> START TRANSACTION;
mysql> SELECT * FROM child WHERE id > 100 FOR UPDATE;

B开始一个事务,往间隙中插入一条记录。事务在等待获得排他锁时,加上插入意向锁。
mysql> START TRANSACTION;
mysql> INSERT INTO child (id) VALUES (101);

8. AUTO-INC Locks
自动增长锁

AUTO-INC锁是一种特别的表级锁,当事务向表中插入自动增长的列时需要获取这种锁。最简单的情形,如果一个事务在向表中插入值,任何其他事务向该表插入数据时必须
等待,这样第一个事务中插入的行才能收到连续的主键值。

innodb_autoinc_lock_mode配置项,控制着自增长锁的算法。它允许你选择如何在可预测的自增值的序列和插入操作的最大并发之间折中。

更多信息,查看 Section 14.8.1.5, “AUTO_INCREMENT Handling in InnoDB”.