社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  DATABASE

如何解决MySQL中的死锁问题?

CDA数据分析师 • 2 年前 • 243 次点击  


导读:虽然锁在一定程度上能够解决并发问题,但稍有不慎,就可能造成死锁。本文介绍死锁的产生及处理。


作者:肖宇 冰河
来源:大数据DT(ID:hzdashuju)


01 死锁的产生和预防

发生死锁的必要条件有4个,分别为互斥条件、不可剥夺条件、请求与保持条件和循环等待条件,如图1-6所示。

▲图1-6 死锁的必要条件

1. 互斥条件

在一段时间内,计算机中的某个资源只能被一个进程占用。此时,如果其他进程请求该资源,则只能等待。

2. 不可剥夺条件

某个进程获得的资源在使用完毕之前,不能被其他进程强行夺走,只能由获得资源的进程主动释放。

3. 请求与保持条件

进程已经获得了至少一个资源,又要请求其他资源,但请求的资源已经被其他进程占有,此时请求的进程就会被阻塞,并且不会释放自己已获得的资源。

4. 循环等待条件

系统中的进程之间相互等待,同时各自占用的资源又会被下一个进程所请求。例如有进程A、进程B和进程C三个进程,进程A请求的资源被进程B占用,进程B请求的资源被进程C占用,进程C请求的资源被进程A占用,于是形成了循环等待条件,如图1-7所示。

▲图1-7 死锁的循环等待条件

需要注意的是,只有4个必要条件都满足时,才会发生死锁。

处理死锁有4种方法,分别为预防死锁、避免死锁、检测死锁和解除死锁,如图1-8所示。

▲图1-8 处理死锁的方法

  1. 预防死锁:处理死锁最直接的方法就是破坏造成死锁的4个必要条件中的一个或多个,以防止死锁的发生。
  2. 避免死锁:在系统资源的分配过程中,使用某种策略或者方法防止系统进入不安全状态,从而避免死锁的发生。
  3. 检测死锁: 这种方法允许系统在运行过程中发生死锁,但是能够检测死锁的发生,并采取适当的措施清除死锁。
  4. 解除死锁:当检测出死锁后,采用适当的策略和方法将进程从死锁状态解脱出来。

在实际工作中,通常采用有序资源分配法和银行家算法这两种方式来避免死锁,大家可自行了解。


02 MySQL中的死锁问题

在MySQL 5.5.5及以上版本中,MySQL的默认存储引擎是InnoDB。该存储引擎使用的是行级锁,在某种情况下会产生死锁问题,所以InnoDB存储引擎采用了一种叫作等待图(wait-for graph)的方法来自动检测死锁,如果发现死锁,就会自动回滚一个事务。

接下来,我们看一个MySQL中的死锁案例。

第一步:打开终端A,登录MySQL,将事务隔离级别设置为可重复读,开启事务后为account数据表中id为1的数据添加排他锁,如下所示。

mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select  * from account where id =1 for update;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张三   |     300 |
+----+--------+---------+
1 row in set (0.00 sec)

第二步:打开终端B,登录MySQL,将事务隔离级别设置为可重复读,开启事务后为account数据表中id为2的数据添加排他锁,如下所示。

mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from account where id  =2 for update;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  2 | 李四   |     350 |
+----+--------+---------+
1 row in set (0.00 sec)

第三步:在终端A为account数据表中id为2的数据添加排他锁,如下所示。

mysql> select * from account where id =2 for update;

此时,线程会一直卡住,因为在等待终端B中id为2的数据释放排他锁。

第四步:在终端B中为account数据表中id为1的数据添加排他锁,如下所示。




    
mysql> select * from account where id =1 for update;
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

此时发生了死锁。通过如下命令可以查看死锁的日志信息。

show engine innodb status\G

通过命令行查看LATEST DETECTED DEADLOCK选项相关的信息,可以发现死锁的相关信息,或者通过配置innodb_print_all_deadlocks(MySQL 5.6.2版本开始提供)参数为ON,将死锁相关信息打印到MySQL错误日志中。

在MySQL中,通常通过以下几种方式来避免死锁。

  1. 尽量让数据表中的数据检索都通过索引来完成,避免无效索引导致行锁升级为表锁。
  2. 合理设计索引,尽量缩小锁的范围。
  3. 尽量减少查询条件的范围,尽量避免间隙锁或缩小间隙锁的范围。
  4. 尽量控制事务的大小,减少一次事务锁定的资源数量,缩短锁定资源的时间。
  5. 如果一条SQL语句涉及事务加锁操作,则尽量将其放在整个事务的最后执行。
  6. 尽可能使用低级别的事务隔离机制。

关于作者:肖宇,分布式事务架构专家,Apache ShenYu(incubating)网关创始人,Dromara开源组织创始人,Hmily、RainCat、Myth等分布式事务框架的作者。Apache ShardingSphere Committer。
冰河,互联网高级技术专家、MySQL技术专家、分布式事务架构专家。多年来,一直致力于分布式系统架构、微服务、分布式数据库、分布式事务与大数据技术的研究,在高并发、高可用、高可扩展性、高可维护性和大数据等领域拥有丰富的架构经验。

本文摘编自深入理解分布式事务:原理与实战》,经出版方授权发布。


 


点这里👇关注我,记得标星哦~


 

推荐阅读

 

CDA课程咨询

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/121962
 
243 次点击