浅谈数据库的锁(二)
前言
 今天这篇文章,讲的主要是下面两个内容:(1)gap lock 和 next - key lock (2)锁,到底是锁的是什么东西?
并且想要解决几个令人困惑的问题
- 在主键,唯一键,普通索引,以及非索引字段加锁,究竟锁住来了什么东西? 
- 不同的查询条件,锁住什么东西? 
- 条件中的等值不存在,锁住什么? 
间隙锁 gap lock
 为什么有间隙锁,其实这个是幻读带来的。产生幻读的原因在之前的文章说过了,(1)通过条件查询 (2)应用层逻辑判断 (3)根据条件进行DML操作。
 如果一个事务符合这三个特点,就很可能有幻读的问题。因为(3)的DML操作的前提条件依赖(1)的一致性试图。但是如果有别的事务,破坏了这个条件,并且在当前事务执行(3)之前先提交了。那么(3)它依赖的条件,虽然在当前条件的视图中看起来是自洽的,但是实际上前提条件已经被破坏。这就是幻读产生的原因。
 因此,需要一个锁,把(1)中查询出来的全部锁住。不仅如此,甚至对”不存在的,并且在条件范围内的也要锁住“,参考创建用户名的例子
 so,gap lock应运而生。
接下的讨论基本都是在此表的基础上展开
| 1 |  | 
gap lock 如何解决幻读
 产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。
 因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。

 然后,如何解决幻读呢?如上图,其实根据(1)条件进行查询之后,会对那些间隙进行加锁。只要间隙上面有了锁,那么其他事务无法对其进行DML操作。
 and,我们还要讨论一下,gap lock阻塞的是什么东西。其实很简单,就两个特点:
- 跟gap lock发生冲突的是 往间隙里面进行插入操作
- gap lock 跟 gap lock之间是不冲突的。- (0,10)和(2,9)或者( 1,11)都是不冲突的
- 间隙之间有无交集都是不冲突的
 
next - key lock 间隙锁升级
     间隙锁和行锁合称 next-key lock,每个 next-key lock 是前开后闭区间。 如果上面,那个表用了select * from t for update。那么就会出现七个next-key lock。
| 1 |  | 
锁到底锁的是什么?
1.以下讨论的都是写锁(排他锁),share mode没必要讨论
2.都是基于上面创建的表,进行讨论
要讨论锁到底锁的是什么,大致要分以下几种情况来讨论:
- 主键(唯一索引),条件命中
- 主键(唯一索引),条件不命中 / 条件模糊
- 普通索引,条件命中
- 普通字段,条件命中
主键,条件命中
| 1 |  | 

ok,我们来看一下,这种情况下的锁是怎么样的。在主键存在的情况下,加锁的情况是这样的:
- 只有命中的那条数据,加上了row lock
- 主键命中的情况不存在gap lock

| 1 |  | 



主键,条件不命中
| 1 |  | 

在条件不命中的情况下,锁是这样的:
- 行与行之间加上gap lock
- 所有行记录都不加row lock
| 1 |  | 

索引,条件命中
| 1 |  | 

普通索引,条件命中的情况下,锁的情况是这样的:
- 命中的,是行锁,行锁加在主键上
- 其余是gap lock,gap lock加在其他索引树上(在该表中,加载索引c的b树上
| 1 |  | 
- 对于查索引命中的行,加的是行锁。


 id = 25,不是查出来的数据,可以更新d字段。
 个人认为,行锁是加在主键上的,这样可以避免 通过别的索引,修改已经查出来的行
- 在对应的索引树上,加gap lock。不影响其他索引树

(1)对于update t set d = 5 where id = 10
 因为,行锁只加在查询出的(id = 0 和 id = 5)两条数据里。gap lock是加载字段c的索引上。当这条记录更新完成,并不会更新字段c的索引树。因此能够运行
(2)对于update t set c = 4 where id = 0
 显然是因为行锁,这语句被阻塞了。
(3)对于update t set c = 5 where id = 10
 id = 10不是查出来的数据,但是对行记录更新完成后,因为操作的是索引字段c,因此需要对字段c的索引树进行修改。修改索引树的过程遇到gap lock被阻塞了
索引,条件不命中
| 1 |  | 

该情况下,加锁的情况:
- 只对整个字段c索引树加gap lock
- 不存在 row lock
原因不解释了,跟上面的差不多
| 1 |  | 
普通字段
表中的数据

| 1 |  | 
对于非索引字段,整个表都是加的表锁
| 1 |  | 
行锁到底锁的是主键还是普通索引?
 为了验证我们的想法,我们需要在原来的表中,新建一个索引。这个索引就建在字段C上面吧
| 1 |  | 


 ok,现在原来的表中有两个普通索引c和d了。
| 1 |  | 
分析:
- 已知,索引树c和索引树d是独立的。
- 如果行锁加载索引树上,那么索引树c上对id=5的加行锁,不影响在索引树d对id=5加行锁
- 如果加载主键上,必然被阻塞。

 ok,索引树d的操作被阻塞了,显然,行锁是加在主键上的。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!