1、行锁
行锁实际上是加在索引上的,因此当执行的加锁的语句的条件列若是没有索引的的时候,此时加的就不是行锁,而是表锁
2、三种行锁
记录锁:MySQL会为该行的索引加上锁,避免其他的并发事务同事操作这一条数据。例如:select * from user where id = 1 for update,此时就会为id=1的这条记录加上记录锁。
间隙锁:它会锁定记录的间隙,间隙锁是不包含该索引记录本身的,是为了那些不存在的记录加锁。例如当前表中有id为1,5,9的三条记录,那么此时就存在(负无穷,1)、(1,5)、(5,9)、(9,正无穷)四个区间,间隙锁可以锁定上面的区间,避免其他并发事务往这些区间插入数据,利用这样的方式去解决RR隔离级别下部分幻读的问题。
临建锁(Next-Key Lock):可以理解为行锁+间隙锁的组合,间隙锁不锁定当前记录本身,而临建锁它实在锁住对应的区间的情况下,还会将当前记录也锁住,为当前记录加上记录锁,而在innodb存储引擎中,RR隔离级别的情况下,默认会采用临建锁去进行加行锁。但是针对不同的情况下,它可能会调整为记录锁、表锁以及间隙锁。
3、锁的调整情况
当对唯一索引去进行匹配时,对一个已经存在的数据去做等值判断,此时使用的是记录锁。
如果条件列没有索引,此时无法加行锁,此时用的就是表锁。
如果对唯一列做等值查询,且该加锁的记录不存在时,那么此时就使用间隙锁。例如表中有id=2,9这两条数据,此时当我们update ... id = 5的时候,那么就会对(2,9)这个区间加一个间隙锁
如果是对非唯一的普通索引做等值查询时,向右遍历的最后一个值不满足查询条件的时候,则会退化为一个间隙锁,这个原因是由于非唯一索引不能仅仅锁住当前记录本身,还要锁住下一个大于当前值的索引的间隙,避免由于插入重复的记录,导致出现幻读。例如有索引age=20,25这两条数据,当我们要对20去进行加锁的时候,由于是非唯一索引,因此还会继续向右遍历数据直到25,会锁住(20,25)之间的间隙,避免因为重复插入20导致出现幻读
如果是对唯一索引进行范围查询时,会一直向范围方向遍历到第一个不满足查询条件的值为止,知道正负无穷大,例如有id=80,88这条件记录,加锁的条件为id>=80,那么此时则会为80加上记录锁,(80,88)以及(88,正无穷)会加上对应的间隙锁,那这也就是组合起来变成临建锁
4、总结
MySQL锁是加在索引记录上面的。
如果是非唯一性索引,不论表中是否存在该记录,除了会对该记录所在范围加锁,还会向右遍历到不满足条件的范围进行加锁。
如果是唯一索引,如果表中存在该记录,只对该行记录加锁。如果表中不存在该记录,除了会对该记录所在范围加锁,还会向右遍历到不满足条件的范围进行加锁。
5、意向锁
一个表中有许多记录,一个事务为表中的某一行数据添加了行锁,另一个事务需要为该表添加表锁的时候,如何知道这个表中还有没有其他的行锁呢?
当需要加行锁的时候,MySQL会先为当前表加上意向锁,若此时有其他事务需要申请表锁的时候,则只需要判断该表是否存在兼容的意向锁即可。不需要遍历所有的行来判断是否存在行锁。
有了意向锁,不能再申请表锁,但是如果锁的不是同一行,其他事务是可以申请行锁的。
6、二级索引的更新加锁过程
- MySQL首先会在二级索引上找到对应的索引记录 并对其加锁
- 然后,因为二级索引中包含了对应行的主键值, MySQL会使用这个主键值来定位主键索引中的记录
- 一旦找到了主键索引中的记录,MySQL会对这 个记录加锁
- 在锁定了必要的索引记录之后,MySQL会执行 实际的数据更新
- 如果更新的列是二级索引的一部分,那么 MySQL还需要更新二级索引中的记录
- 更新完成后,MySQL会释放所有在此过程中获得所有锁