用动态的观点看加锁
前言
间隙锁的两原则两优化一bug
原则1:加锁的基础是加next-lock 即间隙锁
原则2:查找过程中访问到的对象都会加锁
优化1:等值查询中,如果是主键索引上的查找,会退化成行锁
优化2:等值查询中,向右遍历时,如果最后一个值不符合,则会退化成间隙锁
一bug:唯一主键的返回查询,必须找到第一个不满足的才会停止
用动态的观点看加锁
- 前言
- 间隙锁的两原则两优化一bug
- 间隙锁的加锁范围
- **示例**
- 哪些查询条件是等值查询,但看起来像范围查询呢?
- **`IN` 查询**(等值集合查询)
- 2. **`BETWEEN` 查询**(等值查询范围)
- 3. **`=`(等值)条件与 `>=` 或 `<=` 结合的查询**
- 4. **某些类型的多列等值查询**
- 5. **前缀索引查询**
- 死锁回滚
间隙锁的两原则两优化一bug
原则1:加锁的基础是加next-lock 即间隙锁
原则2:查找过程中访问到的对象都会加锁
优化1:等值查询中,如果是主键索引上的查找,会退化成行锁
优化2:等值查询中,向右遍历时,如果最后一个值不符合,则会退化成间隙锁
一bug:唯一主键的返回查询,必须找到第一个不满足的才会停止
间隙锁的加锁范围
间隙锁的加锁范围是基于查询条件和起始位置
以及锁的方向
其中锁的方向可以这样理解,由于在范围查询(1,5)时,查询到5之后,并不知道5之后是不是下一条数据还是5,所以会继续往后扫描一下扫描到7,那么认为范围已经确定。
但是根据原则2,查找过程中访问到的对象都会加锁,所以5和7之间的间隙也会被加上间隙锁。但是0和1之间不会。
示例
假设表中有以下数据:
id | value |
---|---|
1 | A |
3 | B |
5 | C |
7 | D |
如果一个事务发起了一个 SELECT FOR UPDATE
查询,查询条件为 id BETWEEN 1 AND 5
,那么 InnoDB 会加锁以下区域:
- 锁定
id = 1
和id = 3
行之间的间隙(即id = 2
的位置)。 - 锁定
id = 3
和id = 5
行之间的间隙(即id = 4
的位置)。 - 并不会锁定
id = 5
行本身,而是锁定了id = 5
和id = 7
之间的间隙。
我们说加锁单位是next-key lock,都是前开后闭区间,如:(0,5]、(5,10]和(10, 15)
哪些查询条件是等值查询,但看起来像范围查询呢?
由于上面提到的等值查询,其实并不仅仅是=的查询还有一些隐式的查询。
IN
查询(等值集合查询)
- 查询条件:
WHERE id IN (1, 2, 3, 4)
- 解释:虽然
IN
语句中的条件看起来像是多个等值条件,但实际上它会被 拆解为多个等值查询,每个等值条件会分别查找一个数据行。这会导致数据库为每个条件锁住一行,最终结果就是 多个行锁。但是,这个查询的锁行为实际上是多次等值查询的合成,锁住了多行,而不是范围查询。 - 特别注意:虽然
IN
是等值查询的组合,但它的锁机制跟单个等值查询类似,只不过是对多个行进行锁定。
2. BETWEEN
查询(等值查询范围)
- 查询条件:
WHERE id BETWEEN 1 AND 10
- 解释:虽然
BETWEEN
通常被视为范围查询,但在某些情况下,数据库可以通过索引优化将其转换为等值查询的多个条件。例如,数据库可能将BETWEEN 1 AND 10
拆解为多个等值查询,如:id = 1 OR id = 2 OR id = 3 ...
,然后用 行锁 锁住每个匹配的行。 - 特别注意:这里并没有使用间隙锁,只要查询的范围没有数据被插入的空隙,查询会退化为对每个行的锁定。
3. =
(等值)条件与 >=
或 <=
结合的查询
- 查询条件:
WHERE id >= 5 AND id <= 10
- 解释:尽管这是一个范围查询,InnoDB 会尝试使用索引进行优化,如果有适合的索引(例如
id
索引),它可以把这种查询当作“等值查询的范围”进行处理。由于它是有索引支持的,可以对符合条件的行进行锁定。 - 特别注意:虽然查询是一个范围查询,但如果查询使用的是主键或者唯一索引,并且符合等值条件的行比较少,数据库可能退化为行锁的情况。也就是说,
WHERE id >= 5 AND id <= 10
在某些情况下不会使用间隙锁,而是退化为对所有符合条件的行加行锁。
4. 某些类型的多列等值查询
- 查询条件:
WHERE (col1 = 'A' AND col2 = 'B')
- 解释:如果
(col1, col2)
是联合索引或主键索引,数据库可以使用复合索引进行查询。在这种情况下,查询实际上是等值查询,只不过是针对多个列的联合索引。即使是复合索引,这种查询也会按照每个匹配条件的行锁来处理。 - 特别注意:此类查询在某些情况下会用行锁替代间隙锁,尤其是当查询范围比较明确时。
5. 前缀索引查询
- 查询条件:
WHERE name LIKE 'A%'
- 解释:如果
name
列上有一个前缀索引,LIKE 'A%'
查询可能会退化为一个范围查询。虽然LIKE
查询通常是范围查询,但在前缀索引的情况下,它可以被优化为对特定范围的等值查询,并加锁这些行。 - 特别注意:这种情况涉及到索引优化,如果数据库能使用索引高效地找到匹配项,则会退化为加行锁。否则,可能会使用间隙锁。
死锁回滚
对于死锁,mysql会根据回滚那个事务代价更小,而回滚那个。