数据库内核系列(二)- MySQL的锁

前言

在InnoDB中,锁分为以下八种。

  • Shared and Exclusive Locks
  • Intention Locks
  • Record Locks
  • Gap Locks
  • Next-Key Locks
  • Insert Intention Locks
  • AUTO-INC Locks
  • Predicate Locks for Spatial Indexes

Shared & Exclusive Locks

先看第一个,shared和exclusive locks。首先要明白他俩的区别,shared locks是针对读操作的,shared lock允许持有锁的事务读取特定行,并且允许多个事务同时持有这个s(shared)lock;而exclusive lock就是我们常说的互斥锁,主要是针对Update操作的(这里指的是广义的Update,包括了delete),持有锁的对象可以修改删除特定行,并且x(exclusive)lock必须要等到当前事务释放锁之后,下一个请求的事务才可以获得锁。

Intention Locks

与前面的两种锁不同的是,intention lock是表级锁,并且同样支持intention shared(IS)与intention exclusive lock(IX)。我们最熟悉的select … for update即获取了一个IX锁;那么相应的获取IS锁的语句,就有select … for share。在获取S锁或者X锁之前,事务必须要拿到一个表级别的IS锁或者IX锁,否则就失败了。获取之后还有重要的一步,就是判断将要授权给这个事务的锁是否与该事务持有的锁冲突,只有两个锁可以共存的情况下,才能往下进行。具体的不同锁之间的关系详见下表,不难发现互斥锁(X锁)是最严格的锁,这也符合我们正常的逻辑,毕竟谁都不想在我们修改数据的时候造成race condition。

关于这里的intention lock有一点要注意,因为将intention lock授权给一个事务是一个两阶段的过程,所以存在死锁的可能性,这个时候error就产生了,有关死锁我会单独开一篇来讲。

总而言之,除了全表的写之外(例如:LOCK TABLES … WRITE),intention lock并不会阻塞任何请求,顾名思义它只是用来表明自己的意图,具体来说,是表明有事务正持有一个行锁,或者表明正在请求获取表中的一个行锁。

Record Locks

Record lock的概念核心在于,它锁住的是这条记录的索引。例如,SELECT a FROM t WHERE a = 1 FOR UPDATE; 这条语句会阻止其他的事务针对 t.a = 1这一行进行修改(Insert,Update,Delete)操作。特别的,如果一个表没有索引的话,InooDB会创建一个隐藏的聚簇主键索引来上锁。和死锁一样,关于索引我也会单独开一章来介绍。

Gap Locks

Gap lock是一种在索引记录间隙上的锁,在第一条索引记录之前,最后一条索引记录之后。Gap lock加锁的原则取决于该索引是否为唯一索引。

对于非唯一索引,当WHERE后面是范围检索时,例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE; 这个语句中,c1是t表中的非唯一索引,SELECT … FOR UPDATE会防止其他的事务在c1的值为10到20区间内的记录上做修改或删除操作,也就是说,它锁住的是(10,20]这个区间段的记录(左开右闭)。而当WHERE后面是匹配条件时,假设我们的t表中有如下数据,

CREATE TABLE t
(
  id int not null primary key,
  c1 int 
);
INSERT INTO t(id,c1) VALUES (1,6);
INSERT INTO t(id,c1) VALUES (3,3);
INSERT INTO t(id,c1) VALUES (10,10);
INSERT INTO t(id,c1) VALUES (13,25);

然后我们再来看如下的语句,SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 对于gap lock来说,这条语句锁住的是c1前后的记录,也就是说,如果我们想插入一条c1=7的记录,gap lock会阻止插入,想要插入一条c1=13的记录,gap lock也会阻止插入。

对于唯一索引,在精确匹配并且在选择返回全部列的情况下,gap lock不会锁住索引,例如,SELECT * FROM t WHERE id = 1; 因为id是唯一索引(主键),这里的规则是不会存在gap lock。然而当返回的是一条或者多条记录的部分列时,gap lock依旧存在,例如,SELECT c1 FROM t WHERE id = 1; 而当唯一索引后面跟的是范围检索时,同样会锁住(a,b]区间内的记录。例如,SELECT * FROM t WHERE id > 10; 这条语句锁住的是10到正无穷的记录,与非唯一索引在范围上加锁是一样的。

Gap lock的最大作用在于防止其他事务在这个索引间隙中插入记录,这就解决了在REPEATABLE READ下幻读的问题。Gap lock是性能和并发控制的一种取舍,只会在REPEATABLE READ下存在。另外,gap lock可以共存,一个事务持有的gap lock也可以被另外一个事务持有,并且shared gap lock与exclusive gap lock并没有任何区别,他们的作用是相同的。如果想要关闭gap lock,就要将事务隔离级别设定为READ COMMITTED。

Next-Key Lock

简单地说,Next-key lock并不是一个全新的锁类型,它只是行锁与gap锁的一个组合锁。在默认的REPEATABLE READ隔离级别下,InnoDB会使用next-key lock来作为默认的锁,并在gap lock部分提到的特定条件下,放开gap lock的限制从而退化为单纯的行锁。

下一章把剩下的几种锁一起讲完,然后开始讲具体的实现。最近手头事情不少,今天就先写到这里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dabtwice

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值