持锁语句 | |
---|---|
S | SELECT |
X | UPDATE / DELETE |
IS | SELECT FOR SHARE |
IX | SELECT FOR UPDATE |
SHARED LOCK / EXCLUSIVE LOCK (row-level)
共享锁允许事务持有读一行的锁;
排他锁允许事务持有更新或删除一行的锁。
如果事务T1持有行r上的共享锁S,那么其他的事务T2获得行r上的锁有如下情况:
T2可以立即获得共享锁S。
T2不能立即获得排他锁X。如果事务T1持有行r上的排他锁X,T2想在行r上获得任何类型的锁都不会立即得到,必须等待T1释放了行r上的排他锁。
INTENTION LOCK (table-level)
InnoDB实现多粒度锁机制,允许表锁和行锁的共存。
InnoDB使用意向锁来实现多粒度锁共存机制。
意向锁是表级锁,它表明这个事务在接下来将对表中的一行请求什么类型的锁(S/X)。
- intention shared lock , 表明事务T将要在表t的个别行上加S锁(将要读取某些行);
例如 select ... lock in share mode - intention exclusive lock , 表明事务T将要在表t的个别行上加X锁(将要 U/D 某些行)。
例如 select ... for update
意向锁的协议如下:
- 事务在获得表中一行的S锁之前,它必须首先获得这张表上的IS锁,或更stronger的锁
- 事务在获得表中一行的X锁之前,它必须首先获得这张表上的IX锁,或更stronger的锁
锁类型的兼容矩阵列表如下
X
IX
S
IS
X
× × × × IX
× √ × √
S
× × √ √ IS
× √ √ √ 当一个事务请求某个类型的锁时,如果这个类型的锁与请求对象上持有的锁相兼容,那么会成功获得请求的锁。
如果请求的锁与当前请求对象上持有的锁相冲突,那么事务就会等待已存在的锁释放。如果等待的这个锁不可能被授予,就会发生死锁。
意向锁的主要作用是表明某个事务正在锁住某一行、或将要锁住某一行。
- intention shared lock , 表明事务T将要在表t的个别行上加S锁(将要读取某些行);
RECORD LOCK
Record lock是在索引记录上的锁。
Record locks总是锁住索引记录,即便一个表没有定义索引。这种情况下InnoDB创建隐藏的集簇索引,用这个隐藏的集簇索引来进行record locking。
GAP LOCK
间隙锁是锁在index records之间间隙上的锁,或者第一个index record之前的间隙、最后一个index record之后的间隙。
例如: SELECT c1 FROM t where c1 BETWEEN 10 and 20 FOR UPDATE ; 这句SQL会阻止其他事务插入c1为15的记录,
根据index record的记录来确定间隙锁的范围
如果10 和 20 都有具体的index record , 那么锁住的范围是10-20 ;
如果10 和 20 没有具体的index record , 那么锁住的范围是 10之前的index record - 20 之后的index record 。
不管是否有这条记录都会阻止插入,因为这个间隙都被锁住了。
一个间隙可能跨越一个单一的索引值、多个索引值、甚至是空。
间隙锁需要综合考虑性能和并发,只在某些事务隔离级别(RR)中才会使用。
对于使用唯一索引查找唯一行、然后进行锁定的语句来说,不需要间隙锁。
(如果查询条件只包含唯一索引的部分列,这种情况仍需要间隙锁)。
例:
SELECT * FROM child WHERE id = 100 ;
如果id列是唯一索引,那么上面的语句将只持有id等于100这一行的record lock,并不阻止其他事务插入数据到该条记录前面的间隙中。
(假设表记录有两条,id为 50 和 100 ,那么这里说的该条记录前面的间隙即表示(50,100] )。如果id列上没有索引,或不是唯一索引,那么这条语句会锁住该条记录之前的间隙。
需要注意的是,不同的事务可以在一个gap上持有冲突的锁。
例如,当事务B持有排他gap lock(gap X-lock)锁,事务A可以同时持有共享gap lock(gap S-lock) 锁。
这种情况的原因是因为,如果一条记录从index中被purge时,在这条记录上,当前事务gap锁和其他事务上的gap锁必须被merged。Gap lock是抑制性的,这表示gap lock只会阻止其他事务插入新的记录到这个gap中。
不会阻止其他的事务在相同gap上获取gap locks。因此,gap x-lock 和 gap s-lock的影响是相同的。gap lock 可以被显式的禁止。
在事务级别更改为read committed , 或innodb_locks_unsafe_for_binlog变量(已废弃)被启用的情况下。
在这种情况,gap locking在查询、索引扫描时会被禁用,只会用来做外键约束检查和重复键检查。
NEXT-KEY LOCK
next-key lock是一个record lock和这个record lock之前的gap lock的组合。
InnoDB在查找或浏览表索引的时候,InnoDB会在遇到的每个index record上加S或X锁。
row-level锁实际上是index-record锁。
index record上的next-key锁同时会影响这个index record之前的gap。
如果一个session在记录R上持有S或X锁,另外的session不能立即往R之前的gap中插入新的record。
当用到的索引是唯一索引时,不会有gap lock,只会在当前行上加锁。
假设一个索引包含记录如下: 10,11,13和20。可能有的next-key locks情况如下:
( -∞ , 10]
(10 , 11]
(11 , 13]
(13 , 20]
(20 , +∞)
PS:(13 , 20] 意为 13 < x <= 20。默认情况下,InnoDB操作是在repeatable read事务隔离级别。在这种隔离级别下,InnoDB使用next-key locks来防止产生幻读。
INSERT INTENTION LOCK
insert intention lock是一种间隙锁,在INSERT操作之前加锁。
多个事务插入相同index gap时,如果不是插入相同位置的记录,彼此不会互相等待。
例如有索引记录4和7,当不同的事务试图分别插入5和6,在获得插入行的X锁之前,会首先获得4和7之间的间隙上的插入意向锁。
但他们不会互相阻塞,因为他们插入的不是相同的行。
AUTO-INC LOCK (table-level)
Auto-inc锁是一种特殊的表级锁,事务插入具有自增列的表时需要获得此锁。
简单的说,就是一个事务正在插入表,那么其他插入表的事务必须等待这个当前插入事务完成,以保证主键值的连续。
配置参数innodb_autoinc_lock_mode控制auto-increment 锁所使用的算法。
predicate locks for spatial indexes
空间索引。
InnoDB为特殊列提供了SPATIAL index。
处理使用SPATIAL indexes时,next-key不能很好的支持REPEATABLE READ或者SERIALIZABLE事务隔离级别。因为对于多维的数据,并没有明确的顺序,因此没有“next-key“的概念。
为了支持SPATIAL indexes表的事务隔离级别,InnoDB使用predicate locks。
SPATIAL index包含 minimum bounding rectangle(MBR)数据,因此在查询时,InnoDB通过在MBR数据上加predicate lock来实现一致性读。如果查询条件一致,其他的session不能插入或修改这条数据。