目录
共享锁与排他锁
innodb实现了两种标准的行级锁,shared(s)锁和exclusive(x)锁
- s锁允许事务读取行时持有锁
- x锁允许持有该锁的事务更新或删
如果t1持有r的s锁,那么t2对r的锁请求处理方式为:
- 如果t2请求s锁,会立即通过,t1,t2同时持有r的s锁
- t2请求x锁,将不会成功。需要等待t1释放
如果t1持有r的x锁。t2申请锁,会被阻塞。
意向锁
innodb支持多粒度加锁,即允许表锁行锁共存。如:lock tables ... write
在一个表上持有x锁。innodb为了使多粒度加锁变得切实可行,采用了意向锁(即使用意向锁支持了多重粒度锁定)。
意向锁是表级锁,用来表事务将要请求对表中某行的x或s锁
补充:没有意向锁的情况下,事务若想对表加x锁,则需要判断该表是否被加锁(行级锁),没有意向锁,则需要全表遍历.有了意向锁,就非常简单的判断是否有行级锁
intension shared lock (is)
声明事务打算对表中某行设置s锁intension exlusive lock (ix)
声明事务打算对表中某行设置x锁
如: select ... for share
设置了个is锁,select ... for update
设置了一个ix锁
意向锁协议:
- 一个事务对表中行加s锁前,必须先加is或者ix锁
- 一个事务对表重行加x锁前,必须先加ix锁
表级锁兼容类型
x | ix | s | is | |
---|---|---|---|---|
x | conlict | conflict | conflict | conflict |
ix | conflit | compatible | conflit | conflict |
s | conflit | conflit | compatible | compatible |
is | conflit | compatible | compatile | compatile |
存在锁时,若事务申请的锁与已存在的锁兼容,则枷锁成功,冲突时,等待锁释放,如果一直不能获得锁,将会导致死锁.
意向锁不会阻塞任何事,触发对全表的请求(lock tables…write).ix和is锁的主要目的为表明某个事务正在锁定某行,或者将要锁定某行
show engine innodb status
和innodb monitor中的事务数据显示为:
1 | TABLE LOCK table `test`.`t` trx id 10080 lock mode IX |
行锁
单条记录上加锁,是锁的索引.举例SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE
,阻止任何其他事务对t.c1=10的行进行插入更新或者删除.
行锁永远是螺柱索引,即使表中没有人为设置索引,innodb会创建一个隐藏的聚集索引,并且使用这个索引记录锁.
行级锁的事务日志在show engine innodb status
和innodb monitor中看起来类似如下内容:
1 | RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` |
间隙锁
间隙锁锁住的为记录索引之间的间隙,还有第一条记录之前,最后一条记录之后的间隙.For example, SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
,将阻止其他事务插入10-20之间的数据.
间隙可能跨越单个索引值,多个索引值,甚至为空
间隙锁是性能和并发性之间权衡折衷的一部分,并且在可重复读隔离级别中使用
对于使用唯一索引锁定行查询唯一行的语句,不需要使用间隙锁(这不包括搜索条件只包含多列唯一索引的某些列的情况;在这种情况下,确实会发生间隙锁定。)例如,如果该id列具有唯一索引,则以下语句仅使用一个具有id值为100的行的索引记录锁定,其他会话是否在前面的间隙中插入行并不重要:
1 | select * from child where id = 100; |
如果id未建立索引,或索引不唯一,则该语句会锁定前面的间隙.
值得注意的是,可以通过不同的事务将冲突的锁保持在间隙上.如:事务a在间隙上持有s锁(gap S-lock),事务b在相同的间隙上持有x锁(gap X-lock).允许冲突的间隙锁的愿意在于如果从索引中清除记录,则必须合并由不同事务保留在记录上的间隙锁.
间隙锁在innodb是纯抑制性
的,意思是间隙锁仅仅是防止其他事务往间隙中插入数据.间隙锁可以共存.一个事务中的间隙锁,不会阻止其他事务对相同的间隙进行加锁.s和x锁之间没有区别,他们不会互相冲突,并且执行相同的功能.
间隙锁可以被显示禁用.当将事务隔离级别改为提交读或启用系统变量innodb_locks_unsafe_for_binlog(已经不建议使用)
时,搜索、索引扫描、外键约束检查、重复键检查时间隙锁将被禁用
将事务隔离级别改为提交读或者启用系统变量innodb_locks_unsafe_for_binlog
还会带来其他影响,mysql评估where条件之后,将会释放不匹配行的行锁.对于update语句,innodb执行半一致
读(semi-consitent read
),他返回最提交的版本给mysql,以便mysql确定行是否符合update的where条件.
next-key锁
键锁是在索引上的行锁和索引之前间隙锁的组合.
innodb搜索或扫描一个表的索引时,在遇到的行的索引上设置s或x锁来执行行级锁.因此,行级锁实际上是索引记录锁.行级锁实际时行的索引锁.idnex records上的next-key锁,也会影响该index-record之前的间隙.next-key锁是一个index-record锁+该index-record之前间隙的间隙锁.如果一个会话R在索引中的记录上具有共享或排他锁 ,则另一会话不能R在索引顺序之前的间隙中插入新的索引记录 。
假设一个索引包含的值为10,11,13,20
,此索引可能的next-key锁涵盖以下间隔.
1 | (negative infinity,10], |
最后的一个间隔,next-key锁锁定索引中最大值上方的间隙,以及值高于索引中任何实际值的”上确界”.上限值不是一个真正的索引记录,所以这个next-key锁实际上只锁定了最大索引值之后的间隙.
默认情况下,innodb为可重复读事务隔离级别.在这种情况下,innodb使用next-keylock来进行搜索和索引扫描,会防止幻读的出现.
Transaction data for a next-key lock appears similar to the following in SHOW ENGINE INNODB STATUS and InnoDB monitor output:
1 | RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` |
插入意向锁
插入意向锁是一种通过所INSERT行插入之前的操作设置的间隙锁.这个锁发出插入的意图信号,多个事务在相同索引间隙,但不在同一位置进行插入操作实,无需互相等待.假设索引值为4,7,单独事务视图插入5和6,在获得插入行的x锁之前,每个事务都使用插入意向锁来锁定4和7之间的间隙.(不是同一行)不会互相阻塞.
auto-inc锁
一个AUTO-INC锁是通过使用AUTO_INCREMENT插入表事务取得一个特殊的表级锁.一个简单的情况,如果一个事务正在向表中插入值,那么任何其他事务都必须等待该表中进行插入操作,以便第一个事务插入的行接收连续的主键值。
该innodb_autoinc_lock_mode 用于配置自增加锁的算法选项控制。它允许您选择如何在可预测的自动递增值序列和插入操作的最大并发性之间进行权衡。
更多信息,参见Section 15.6.1.6, “AUTO_INCREMENT Handling in InnoDB
空间索引的predicate锁
InnoDB支持SPATIAL包含空间列的列的索引Section 11.4.9, “Optimizing Spatial Analysis
要处理涉及空间索引的操作的锁定,next key locking不能很好地支持可重复读取或可序列化事务隔离级别。多维数据中没有绝对的排序概念,因此不清楚哪个是“next” key。
为了支持具有空间索引的表的隔离级别,InnoDB使用谓词锁。空间索引包含最小边界矩形(MBR)值,因此InnoDB通过在用于查询的MBR值上设置谓词锁来强制索引的一致读取。其他事务无法插入或修改与查询条件匹配的行。