正文

在上一篇文章中,我们简单了解了一下innodb的行级锁(s锁、x锁)表级锁(is锁、ix锁)的概念以及锁之间的兼容关系。

本文,将了解一下innodb的几种加锁的情况:

常见的加锁

1)对于update、delete、insert这种涉及到commit操作的语句,innodb自动会给相关的数据集加上排它锁(X锁)。

2)对于普通的select语句,innodb默认是不会加锁的。但是,一个事务中我们可以显示地给select语句加上共享锁(S锁)或者排它锁(X锁)。这里注意,必须开启事务,在begin和commit/rollback之间才生效。

  2-1)共享锁(S锁),例如:  

select * from t_table where...lock in share mode

  其它事务仍然可以查询记录,或者也加上share mode地共享锁,但不可以加互斥锁。同时要注意,如果当前事务加了共享锁,又准备对该数据进行更新操作(加排它锁),那么可能就造成了死锁

  2-2)排它锁(X锁),例如:

select * from t_table where...for update

  其它事务可以查询该记录,但是不能对该记录加共享锁或者排它锁,只能等待该锁的释放。

如何实现加锁

  mysq的innodb默认采用的是行级锁,当有明确指定并使用索引的时候采用行级锁,否则就是表级锁。

行级锁是通过给索引项加锁来实现的。这就意味着,只有通过索引条件检索数据,innodb才会使用行级锁,否则innodb将使用表锁

主键索引、唯一索引或者是普通索引,innodb都会使用行级锁来对数据加锁。

执行计划真正使用了索引,才能使用行级锁:即便在条件中使用了索引字段,但是否使用索引来检索数据是由mysql通过判断不同执行计划的代价来决定的,如果mysql认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下innodb将使用表锁,而不是行锁。因此,在分析锁冲突的时候,需要检查sql的执行计划,以确认是否使用了索引。

  由于mysql的行级锁是针对索引加的锁,而不是针对记录加的锁,所以虽然多个session是访问不同计划,但是如果是使用相同的索引键,是会出现锁冲突的。

间隙锁

  再来简单了解一下间隙锁。当我们使用范围条件而不是相等条件检索数据的时候,这时候如果请求了共享锁或者排它锁,innodb会给符合条件的已有数据记录的索引项加锁。但是范围内的数据并不是都存在的,对于不存在的数据就叫做"间隙(gap)"。innodb除了对已经存在的索引项加锁,还会对不存在的"间隙"加锁,这种锁机制就叫做间隙锁。

  很显然,在使用范围条件检索并锁定记录的时候,innodb这种加锁机制会阻塞符合条件范围内键值的并发插入,这往往会造成严重的等待。因此,在实际应用开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件。

总结

  到这里,我们简单总结一下。mysql涉及到事务的就会采用innodb引擎,也是默认的引擎。innodb引擎支持行级锁和表级锁两种级别,默认采用行级锁。

  行级锁的触发条件需要执行计划确定使用索引项,也就是行级锁只针对索引加锁而不是数据加锁,这与Oracle存在本质区别。如果不是行级锁的触发,那么就将使用表级锁,也就是意向锁IS和IX。

  最后,我们了解了一下范围条件加锁的时候,对于不存在的"间隙"数据会加上间隙锁。间隙锁将影响插入等待问题,需要适当避免。

 

参考文章:https://zhuanlan.zhihu.com/p/29150809