MySQL 遇到的死锁问_Mysql 百问系列:死锁是怎么发生的

本文深入探讨了数据库中的共享锁(S锁)和独占锁(X锁),解释了它们的加锁机制以及死锁的产生原因。通过一个具体的MySQL死锁示例,展示了事务A和B如何陷入死锁状态。死锁发生时,MySQL会自动回滚其中一个事务以解除死锁。此外,文章强调了减少长事务的重要性,以降低死锁概率,并提出了并发场景下减库存操作可能导致死锁的问题,引发思考如何避免此类死锁。
摘要由CSDN通过智能技术生成

本文主要从共享锁(S锁)和独占锁(X锁)出发,详细说明两种锁的加锁机制,以及死锁如何产生。

问题:

什么是共享锁,什么是独占锁?

死锁怎么发生的?

发生死锁了,Mysql 如何处理的?

共享锁和独占锁

上一篇文章中我们已经讲解了共享锁和独占锁的基本概念,我这边再详细将一下。

共享锁 Shared Locks (S 锁)  从名字就可以看出来它允许共享,共享的意思是当你给某个事物加上锁后,其他人还可以为它加上(仅限)共享锁。

独占锁 Exclusive Locks (X 锁), 如果想要修改某个事物,必须先加上X锁,但是如果该事物已经错在了S锁或者X锁就必须等已经存在的锁释放了才能再加上去。

S锁

X锁

S锁

兼容

不兼容

X锁

不兼容

不兼容

了解了S锁和X锁,那么我们来看看什么是死锁吧。

让我们来复现下死锁的过程,以便更好理解。建议可以根据自己理解来复现死锁过程,并分析原因加深理解。不然变成:

眼睛: 我会了。

脑子: 不,你不会。

准备工作

CREATE TABLE `student` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`name` varchar(255) NOT NULL,

`age` int(11) NOT NULL,

`addresss` varchar(255) NOT NULL DEFAULT '',

PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT student VALUES (1,'张一',12,'ssss'),(2,'张二',12,'ssss'),(3,'张三',12,'ssss'),(4,'张四',12,'ssss'),(5,'张五',12,'ssss');

复制代码

准备工作完成,建立表,并插入5条数据。

模拟死锁第一步

新建一个数据库连接并执行:

###事务A

begin;

select * from student where id in (1,2) lock in share mode;

复制代码

步骤一 事务A 得到了id 为1,2的S锁。

再新建一个数据库连接并执行:

###事务B

begin;

select * from student where id in (1,3) lock in share mode;

复制代码

步骤二 这时事务B 得到了id为1,3的S锁

然后我们在事务B 中尝试获取数据id = 2 的X锁

事务B

update student set age =100 where id = 2

复制代码

步骤三, 由于id为2 被事务A加了S锁,所以事务B 被阻塞。

最后我们在事务A中尝试获取数据id = 3 的X锁

事务A

update student set age =100 where id = 3

#执行结果:1213 - Deadlock found when trying to get lock; try restarting transaction, Time: 0.005000s

复制代码

步骤四,由于id为3 被事务B加了S锁,所以事务A被阻塞。 当然其实执行的时候被跳出死锁警告。事务A回滚撤销了。

到此,我们模拟了整个死锁过程,由于B 为了获得X锁需要等A执行完成,而A后续反而为了获得X锁需要等B执行完成。所以造成了死锁。

死锁的原因

从上面的例子中我们可以看到死锁的原因是相互寻求各自手上的资源(即锁)导致的。

但是其中有个重要的特性需要说明就是二阶段锁协议。

二阶段锁协议很简单:

需要查询或者修改时才加锁。

事务提交的时候才释放锁。

上面例子中步骤三,B之所以会被阻塞,是因为事务A虽然读取数据结束了,但是事务还没结束,所以导致读取是加的锁没有释放。

也是由于二阶段锁协议的原因,所以我们应该尽可能避免长事务。因为`事务执行时间越长,加锁的时间越长,发生死锁的概率越大```

死锁如何处理

mysql 可以设置两种处理方式:

第一种就是上面例子锁展示的,发生死锁时,自动回滚其中一个事务,解除死锁。 可以通过innodb_deadlock_detect 参数设置为on 开启。默认情况下就是on 状态。

设置超时时间,也就是发生死锁时先等待一段时间后,再退出。可以设置innodb_lock_wait_timeout 设置超时时间。

一般情况下,我们会用第一种方式。

留个问题

现实场景中有哪些容易发生死锁的场景呢?其中最常见的一个例子是减库存了。

扣减库存我们一般有以下步骤

查看现有库存是否充足

库存充足则减去库存。 不足则返回扣除失败。

假设流程的sql语句如下

# 查询剩余库存

select last_number from stock where id = 1000 lock in share mode;

#库存足够 --减去库存

update stock set last_number = last_number - 1 where id = 1000;

复制代码

那么问题是:

当并发比较高的时候,上面这个业务功能会不会发生死锁,分析下发生死锁的原因?

为什么要用lock in share mode 加锁,不加锁行不行? 高并发下,如果不加锁会发生什么问题?

怎么修改才能不发生死锁,又能避免其他问题?

问题答案下篇文章回答。有收获,请关注下吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值