一、什么是死锁

死锁是并发系统中常见的问题,同样也会出现在数据库MySQL的并发读写请求场景中。当两个及以上的事务,双方都在等待对方释放已经持有的锁或因为加锁顺序不一致造成循环等待锁资源,就会出现“死锁”。常见的报错信息为 ” Deadlock found when trying to get lock... ”。

加锁(Locking)是数据库在并发访问时保证数据一致性和完整性的主要机制。任何事务都需要获得相应对象上的锁才能访问数据,读取数据的事务通常只需要获得读锁(共享锁),修改数据的事务需要获得写锁(排他锁)。当两个事务互相之间需要等待对方释放获得的资源时,如果系统不进行干预则会一直等待下去,也就是进入了死锁(deadlock)状态。

举例来说 A 事务持有 X1 锁 ,申请 X2 锁,B事务持有 X2 锁,申请 X1 锁。A 和 B 事务持有锁并且申请对方持有的锁进入循环等待,就造成了死锁。

从死锁的定义来看,MySQL 出现死锁的几个要素为:

a.两个或者两个以上事务

b.每个事务都已经持有锁并且申请新的锁

c.锁资源同时只能被同一个事务持有或者不兼容

d.事务之间因为持有锁和申请锁导致彼此循环等待

InnoDB 锁类型

为了分析死锁,我们有必要对 InnoDB 的锁类型有一个了解。

二、数据库表准备

1、创建数据库表

#创建数据库
create database biyu;
#选择数据库
use biyu;
#创建测试表
CREATE TABLE `user` (
  `id` INT ( 11 ) PRIMARY KEY NOT NULL auto_increment,
  `mobile` VARCHAR(45) NULL,
    `name` VARCHAR ( 32 ),
    `address` VARCHAR ( 32 )
);
#插入测试数据
INSERT INTO `user` (`name`, `mobile`,`address`) VALUES ('Lucy', '13688889999','haikou');

2、死锁场景演示

2.1、打开一个dos窗口

  • mysql -u账号 -p密码,进入mysql命令行输入:

  • 开启一个事务,mysql> begin;

  • 执行更新语句,mysql> update user set name = 'Lily' where id =1;

注意:此时更新是成功的,但并未提交事务。

2.2、再打开一个dos窗口

  • 登录mysql,开启另一个事务操作同上。

再次执行更新语句,mysql> update user set name = 'Lilei' where id =1;

注意:此时第二个窗口处于等待中,需要等待第一个窗口释放写锁才能执行成功。

  • 等待一段时候后,第二个窗口报错了

由于第一个窗口未提交事务,导致第二个窗口在等待一段时候后,超过锁定等待超时。

三、解决死锁问题

  • 通过mysql客户端工具连接,按照以下步骤执行。

show status like 'innodb_row_lock%'; #确认有没有锁等待:

+-------------------------------+-------+
| Variable_name                 | Value |
+-------------------------------+-------+
| Innodb_row_lock_current_waits | 0     |
| Innodb_row_lock_time          | 81851 |
| Innodb_row_lock_time_avg      | 40925 |
| Innodb_row_lock_time_max      | 50352 |
| Innodb_row_lock_waits         | 2     |
+-------------------------------+-------+

注意:超过锁定等待超时后,此条记录会消失,也就是说正在阻塞的记录在此表才能查看到。

select * from information_schema.innodb_trx; # 查询锁事务状态信息

查询出死锁后,得到trx_mysql_thread_id死锁的id。

kill sessionId; # 杀掉有问题的session

kill 14;
kill 8;

再次执行select * from information_schema.innodb_trx; # 查询锁事务状态信息

确认此表是否存在锁事务记录,如果没有则说明死锁问题已经解决。

四、如何避免死锁

  • 对于数据库的多表操作时,尽量按照相同的顺序进行处理,尽量避免同时锁定两个资源,如操作A和B两张表时,总是按先A后B的顺序处理, 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源。

  • 把SELECT放在Update语句前

  • SQL语句中不要使用太复杂的关联多表的查询;使用“执行计划”对SQL语句进行分析,对于有全表扫描的SQL语句,建立相应的索引进行优化。

  • 所有的update和delete操作必须走唯一索引

  • 避免事务中的用户等待交互

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐