InnoDB关于事务、锁、MVCC专题( 三 )


一般我们看死锁日志时,都是找关键词,比如lock_mode X locks rec but not gap,就表示一个X型的记录锁 。记录锁的关键词就是rec but not gap 。
间隙锁为了解决幻读问题,InnoDB引入了间隙锁(Gap Lock) 。间隙锁是一种加在两个索引之间的锁,或者加在第一个索引之前,或最后一个索引之后的间隙 。它锁住的是一个区间,而不仅仅是这个区间中的每一条数据 。
比如lock_mode X locks gap before rec表示X型gap锁 。
临键锁Next-key锁是记录锁和间隙锁的组合,它指的是加在某条记录以及这条记录前面间隙上的锁 。说得更具体一点就是:临键锁会封锁索引记录本身,以及索引记录之前的区间,即它的锁区间是前开后闭,比如(5,10]
如果一个会话占有了索引记录R的共享/排他锁,其他会话不能立刻在R之前的区间插入新的索引记录 。
插入意向锁插入意向锁,是插入一行记录操作之前设置的一种间隙锁 。这个锁释放了一种插入方式的信号 。它解决的问题是:多个事务,在同一个索引,同一个范围区间插入记录时,如果插入的位置不冲突,就不会阻塞彼此 。
假设有索引值4、7,几个不同的事务准备插入5、6,每个锁都在获得插入行的独占锁之前用插入意向锁各自锁住了4、7之间的间隙,但是不阻塞对方因为插入行不冲突
自增锁自增锁是一种特殊的表级别锁 。它是专门针对AUTO_INCREMENT类型的列,对于这种列,如果表中新增数据时就会去持有自增锁 。简言之,如果一个事务正在往表中插入记录,所有其他事务的插入必须等待,以便第一个事务插入的行,是连续的主键值 。
自增锁是一个表级别锁,那为什么会话A事务还没结束,事务会话B可以执行插入成功呢?不是应该锁表嘛?
这是因为在参数innodb_autoinc_lock_mode上,这个参数设置为1的时候,相当于将这种auto_inc lock弱化为了一个更轻量级的互斥自增长机制去实现,官方称之为mutex
不同事务RR和RC下加锁的规则

  1. 在RC(读已提交) 的隔离级别下,对查询条件是主键id的场景,会加一个排他锁(X锁),或者说加一个X型的记录锁 。
  2. 在RC(读已提交) 的隔离级别下,对查询条件是唯一索引的场景,该SQL需要加两个X锁,一个对应于 唯一索引上的记录,另一把锁对应于聚簇索引上(主键索引) 。
为什么主键索引上的记录也要加锁呢?
如果并发的一个SQL,是通过主键索引来,此时,如果delete语句没有将主键索引上的记录加锁,那么并发的update就会感知不到delete语句的存在,违背了同一记录上的更新/删除需要串行执行的约束 。
3. 在RC(读已提交) 的隔离级别下,对查询条件是普通索引的场景,那么对应的所有满足SQL查询条件的记录,都会加上锁 。同时,这些记录对应主键索引,也会上锁
4. 在RC(读已提交) 的隔离级别下,对查询条件是无索引(只是一个常规的列)的场景,MySQL会走聚簇索引进行全表扫描过滤 。每条记录都会加上X锁 。但是,为了效率考虑,MySQL在这方面进行了改进,在扫描过程中,若记录不满足过滤条件,会进行解锁操作
5. 在RR(可重复读)的隔离级别下,对查询条件是主键id的场景,会加一个排他锁(X锁),或者说加一个X型的记录锁 。和RC是一样的
6. 在RR(可重复读)的隔离级别下,对查询条件是唯一索引的场景,该SQL需要加两个X锁,一个对应于 唯一索引上的记录,另一把锁对应于聚簇索引上(主键索引) 。和RC是一样的

经验总结扩展阅读