Innodb锁的类型
行锁(record lock)
行锁总是对索引上锁,如果某个表没有定义索引,mysql就会使用默认创建的聚集索引,行锁有S锁和X锁两种类型。
共享锁和排它锁
Innodb锁有两种类型:共享锁(S lock)和排它锁(X lock)
- 不同事务可以同时对同一行记录加S锁
- 如果一个事务对某一行记录加X锁,其他事务就不能加S锁或者X锁,从而导致锁等待
意向锁(intention lock)
Innodb为了支持多粒度的加锁,允许行锁和表锁同时存在。插入意向锁即是一种表锁,在事务对某一行或者多行加锁之前,必须对这张表加入意向锁(或更强的锁如X锁)。插入意向锁分为:
- intention shared lock (IS):表明事务对于这张表的记录有插入S锁的意向
- intention exclusive lock (IX):表明事务对于这张表的记录有插入X锁的意向
插入意向锁和其他表锁兼容性如下:
X | Conflict | Conflict | Conflict |
IX | Conflict | Compatible | Conflict |
S | Conflict | Conflict | Compatible |
IS | Conflict | Compatible | Compatible |
间隙锁(gap lock)
间隙锁锁住一个间隙以防止插入。假设索引列有(a,b,c)三个值,如果对b加锁,那么也会同时对(a,b)和(b,c)这两个间隙加锁。其他事务无法插入索引值在这两个间隙之间的记录(即a<索引值<b或者b<索引值<c都无法插入)。但是,间隙锁有一个例外:
- 如果索引列是唯一索引,那么只会锁住这条记录(只加行锁),而不会锁住间隙。
- 对于联合索引且是唯一索引,如果where条件只包括联合索引的一部分,那么依然会加间隙锁。
对于间隙锁来说,S锁或者X锁没有区别,它们也不会相互冲突。假设索引列有(a,b,c,d)四个值,事务A对b加X锁,也会同时对(a,b)和(b,c)这两个间隙加X锁。其他事务B可以同时对c加X锁,也会同时对(b,c)和(c,d)这两个间隙加X锁。可以看到两个事务都可以对(b,c)这个间隙加X锁,但是不会引起冲突。
next-key lock
next-key lock实际上就是行锁+这条记录前面的gap lock的统称。假设有索引值10, 11, 13, 和20,那么可能的next-key lock包括:
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
在REPEATABLE READ隔离级别下,InnoDB使用next-key lock主要是防止幻像问题(phantom problem)的产生。
插入意向锁(Insert Intention Lock)
插入意向锁指的是事务在对一个gap插入之前会对这个gap加插入意向锁。只要不同事务对于同一个gap插入的位置不同,那么就可以对同一个gap同时加插入意向锁。设有某列有索引值2,6,只要两个事务插入位置不同(如事务A插入3,事务B插入4),那么就可以同时插入。
自增锁(AUTO-INC Lock)
自增锁是一种特殊的表锁。一般说来,为了实现列的自增,不同事务对自增列的插入都会导致锁等待。不过后面mysql对于自增锁做了大量优化,提供了不同算法以实现性能和数据一致的平衡。具体可以参考:https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html
Innodb加锁分析
详细参考:mysql InnoDB加锁分析
Innodb锁等待排查
目前Innodb提供的锁等待排查命令有下面几个:
SHOW FULL PROCESSLIST ;
SHOW ENGINE INNODB STATUS ;
#更为详细
select * from information_schema.INNODB_TRX;
select * from information_schema.INNODB_LOCKS;
select * from information_schema.INNODB_LOCK_WAITS;
通常我使用如下sql来获取必要的信息:
select l.*, t.trx_state,t.trx_weight,t.trx_query,w.* from information_schema.INNODB_TRX t left join information_schema.INNODB_LOCKS l
on t.trx_id=l.lock_trx_id left join information_schema.INNODB_LOCK_WAITS w
on t.trx_id=w.requesting_trx_id;
排查方式可以参考:https://dev.mysql.com/doc/refman/5.7/en/innodb-information-schema-examples.html
Innodb一致性非锁定读(consistent nonblocking read)
前面一个章节讲的是锁定读以保证更新插入数据的一致性,mysql同时也提供不加锁的方式读取数据。mysql使用多版本并发控制(MVCC)来读取数据,如果事务A对某一行正在执行update或delete操作,那么事务B不会读取正在更新的这一行,而是会去读取这行的一个快照数据。

该实现是通过undo段完成,因为undo段用来事务的回滚,因此本身没有额外的开销。
不同的事务隔离级别下,读取的快照版本也不相同
- READ COMMITTED级别下,一致性非锁定读总是读取被锁定行的最新一份快照数据。所以在这个隔离级别下,同一个事务多次读取同一条记录,返回的结果可能不同,因为存在其他事务提交对这一行更新的可能。
- REPEATABLE READ级别下,一致性非锁定读总是读取被锁定行事物开始时的行数据版本。同一个事务多次读取同一条记录返回相同的结果。