您的位置: 首页> 数据库> MySQL 30 用动态的观点看加锁

MySQL 30 用动态的观点看加锁

时间:2025-09-01 14:51:13 来源:互联网

首先复习一下加锁规则:

接下来的讨论基于下表t:

CREATE TABLE `t` (  `id` int(11) NOT NULL,  `c` int(11) DEFAULT NULL,  `d` int(11) DEFAULT NULL,  PRIMARY KEY (`id`),  KEY `c` (`c`)) ENGINE=InnoDB;insert into t values(0,0,0),(5,5,5),(10,10,10),(15,15,15),(20,20,20),(25,25,25);

不等号条件里的等值查询

等值查询和遍历有什么区别?为什么当where条件是不等号,这个过程也有等值查询?

begin;select * from t where id>9 and id<12 order by id desc for update;

利用加锁规则,这个语句的加锁范围是主键索引上的(0,5]、(5,10]、(10,15)。id=15没有加上行锁是因为用到了优化2,退化为了间隙锁。

但是查询语句里where条件不是等号,这里的等值查询又是从哪来的呢?

分析索引id的示意图:

也就是说,在执行过程中,通过树搜索方式定位记录时用的是等值查询的方法。

等值查询的过程

下面这个语句的加锁范围是什么呢?

begin;select id from t where c in(5,20,10) lock in share mode;

先看语句的explain结果:

该语句使用了索引c并且rows=3,说明这三个值都是通过B+树搜索定位的。

在查找c=5时,先锁住了(0,5],但因为c不是唯一索引,为了确认还有没有其他c=5的记录,需要向右遍历,直到c=10才确认没有,该过程满足优化2,所以加间隙锁(5,10)。

同样的,执行c=10 的时候,加锁的范围是(5,10]和(10,15);执行c=20的时候,加锁的范围是(15,20]和(20,25)。

这些锁是在执行过程中一个一个加的,而不是一次性加上去的。

假设同时有另外一个语句:

select id from t where c in(5,20,10) order by c desc for update;

间隙锁不互锁,但这两条语句都会在索引c上的c=5、10、20三行记录上加记录锁。

由于两条语句要加锁相同的资源,但加锁顺序相反,当这两条语句并发执行的时候,就可能出现死锁。

关于死锁的信息,MySQL只保留了最后一个死锁的现场,但这个现场还是不完备的。接下来就分析上面例子的死锁现场。

怎么看死锁?

出现死锁后,执行show engine innodb status命令能输出很多信息,其中有一节LATESTDETECTED DEADLOCK,就是记录的最后一次死锁信息。

该结果分为三部分:

第一个事务的信息中:

第二个事务的信息中:

从上面这些信息中能知道:

因此导致死锁,由此得到结论:

怎么看锁等待?

看完死锁,再看一个锁等待的例子。

由于session A并没有锁住c=10,所以session B删除这一行是可以的,但之后再想insert这一行回去就不行了。

此时执行show engine innodb status,锁信息是在TRANSACTIONS这一节:

由此可知,delete操作删除了id=10的行,原来的间隙(5,10)、(10,15)变成了(5,15)。

有个结论:所谓间隙,其实是由“这个间隙右边的记录”定义的。

update的例子

再看一个update语句的案例:

session A的加锁范围是索引c上的(5,10]、(10,15]、(15,20]、(20,25]、(25,supremum]。

session B第一个语句要把c=5改为c=1,可以理解为两步:

根据上面的结论,间隙是由间隙右边的记录定义,此时session A加锁范围变为:

session B的第二个语句拆成两步:

第一步试图在间隙锁(1,10)插入数据,被堵住。

上一篇:MySQL 29 如何判断一个数据库是不是出问题了? 下一篇:没有了

相关文章

相关应用

最近更新