MySQL死锁如何解决

发布时间:2021-08-13 14:47:26 作者:Leah
来源:亿速云 阅读:133

MySQL死锁如何解决,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

环境准备

数据库隔离级别:

  mysql> select @@tx_isolation;   +-----------------+   | @@tx_isolation  |   +-----------------+   | REPEATABLE-READ |   +-----------------+   1 row in set, 1 warning (0.00 sec)

自动提交关闭:

  mysql> set autocommit=0;   Query OK, 0 rows affected (0.00 sec)      mysql> select @@autocommit;   +--------------+   | @@autocommit |   +--------------+   |            0 |   +--------------+   1 row in set (0.00 sec)

表结构:

  //id是自增主键,name是非唯一索引,balance普通字段   CREATE TABLE `account` (    `id` int(11) NOT NULL AUTO_INCREMENT,     `name` varchar(255) DEFAULT NULL,     `balance` int(11) DEFAULT NULL,     PRIMARY KEY (`id`),     KEY `idx_name` (`name`) USING BTREE   ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

表中的数据:

MySQL死锁如何解决

模拟并发

开启两个终端模拟事务并发情况,执行顺序以及实验现象如下:

MySQL死锁如何解决

1)事务A执行更新操作,更新成功

mysql> update account set balance =1000 where name ='Wei'; Query OK, 1 row affected (0.01 sec)

2)事务B执行更新操作,更新成功

mysql> update account set balance =1000 where name ='Eason';  Query OK, 1 row affected (0.01 sec)

3)事务A执行插入操作,陷入阻塞~

mysql> insert into account values(null,'Jay',100);

MySQL死锁如何解决


这时候可以用

select*frominformation_schema.innodb_locks;

查看锁情况:

MySQL死锁如何解决

4)事务B执行插入操作,插入成功,同时事务A的插入由阻塞变为死锁error。

mysql> insert into account values(null,'Yan',100);  Query OK, 1 row affected (0.01 sec)

MySQL死锁如何解决

锁介绍

在分析死锁日志前,先做一下锁介绍,哈哈~

MySQL死锁如何解决

主要介绍一下兼容性以及锁模式类型的锁:

共享锁与排他锁

InnoDB 实现了标准的行级锁,包括两种:共享锁(简称 s 锁)、排它锁(简称 x 锁)。

如果事务 T1 持有行 r 的 s 锁,那么另一个事务 T2 请求 r 的锁时,会做如下处理:

如果 T1 持有 r 的 x 锁,那么 T2 请求 r 的 x、s 锁都不能被立即允许,T2 必须等待T1释放 x  锁才可以,因为X锁与任何的锁都不兼容。

MySQL死锁如何解决

意向锁

比如:事务1在表1上加了S锁后,事务2想要更改某行记录,需要添加IX锁,由于不兼容,所以需要等待S锁释放;如果事务1在表1上加了IS锁,事务2添加的IX锁与IS锁兼容,就可以操作,这就实现了更细粒度的加锁。

InnoDB存储引擎中锁的兼容性如下表:

MySQL死锁如何解决

记录锁(Record Locks)

记录锁的事务数据(关键词:lock_mode X locks rec butnotgap),记录如下:

  RECORD LOCKS space id 58 page no 3 n bits 72 index `PRIMARY` of table `test`.`t`    trx id 10078 lock_mode X locks rec but not gap   Record lock, heap no 2 PHYSICAL RECORD: n_fields 3; compact format; info bits 0    0: len 4; hex 8000000a; asc     ;;    1: len 6; hex 00000000274f; asc     'O;;    2: len 7; hex b60000019d0110; asc        ;;

间隙锁(Gap Locks)

间隙锁的事务数据(关键词:gap before rec),记录如下:

  RECORD LOCKS space id 177 page no 4 n bits 80 index idx_name of table `test2`.`account`    trx id 38049 lock_mode X locks gap before rec   Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0    0: len 3; hex 576569; asc Wei;;    1: len 4; hex 80000002; asc     ;;

Next-Key Locks

插入意向锁(Insert Intention)

事务数据类似于下面:

  RECORD LOCKS space id 31 page no 3 n bits 72 index `PRIMARY` of table `test`.`child`   trx id 8731 lock_mode X locks gap before rec insert intention waiting   Record lock, heap no 3 PHYSICAL RECORD: n_fields 3; compact format; info bits 0    0: len 4; hex 80000066; asc    f;;    1: len 6; hex 000000002215; asc     " ;;    2: len 7; hex 9000000172011c; asc     r  ;;...

锁模式兼容矩阵(横向是已持有锁,纵向是正在请求的锁):

MySQL死锁如何解决

如何读懂死锁日志?

show engine innodb status

可以用 show engine innodb status,查看最近一次死锁日志哈~,执行后,死锁日志如下:

  0x243c   *** (1) TRANSACTION:   TRANSACTION 38048, ACTIVE 92 sec inserting   mysql tables in use 1, locked 1   LOCK WAIT 4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2   MySQL thread id 53, OS thread handle 2300, query id 2362 localhost ::1 root update   insert into account values(null,'Jay',100)   *** (1) WAITING FOR THIS LOCK TO BE GRANTED:   RECORD LOCKS space id 177 page no 4 n bits 80 index idx_name of table `test2`.`account`   trx id 38048 lock_mode X locks gap before rec insert intention waiting  Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0   0: len 3; hex 576569; asc Wei;;   1: len 4; hex 80000002; asc     ;;    *** (2) TRANSACTION:  TRANSACTION 38049, ACTIVE 72 sec inserting, thread declared inside InnoDB 5000  mysql tables in use 1, locked 1  5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2  MySQL thread id 52, OS thread handle 9276, query id 2363 localhost ::1 root update  insert into account  values(null,'Yan',100)  *** (2) HOLDS THE LOCK(S):  RECORD LOCKS space id 177 page no 4 n bits 80 index idx_name of table `test2`.`account`   trx id 38049 lock_mode X locks gap before rec  Record lock, heap no 6 PHYSICAL RECORD: n_fields 2; compact format; info bits 0   0: len 3; hex 576569; asc Wei;;   1: len 4; hex 80000002; asc     ;;    *** (2) WAITING FOR THIS LOCK TO BE GRANTED:  RECORD LOCKS space id 177 page no 4 n bits 80 index idx_name of table `test2`.`account`   trx id 38049 lock_mode X insert intention waiting  Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0   0: len 8; hex 73757072656d756d; asc supremum;;    *** WE ROLL BACK TRANSACTION (1)

我们如何分析以上死锁日志呢?

第一部分

1)找到关键词TRANSACTION,事务38048

MySQL死锁如何解决

2)查看正在执行的SQL

insert into account values(null,'Jay',100)

3)正在等待锁释放(WAITING FOR THIS LOCK TO BE GRANTED),插入意向排他锁(lockmode X locks gap  before rec insert intention waiting),普通索引(idxname),物理记录(PHYSICAL  RECORD),间隙区间(未知,Wei);

MySQL死锁如何解决

第二部分

1)找到关键词TRANSACTION,事务38049

MySQL死锁如何解决

2)查看正在执行的SQL

insert into account values(null,'Yan',100)

3)持有锁(HOLDS THE LOCK),间隙锁(lockmode X locks gap before rec),普通索引(index  idxname),物理记录(physical record),区间(未知,Wei);

MySQL死锁如何解决

4)正在等待锁释放(waiting for this lock to be granted),插入意向锁(lockmode X insert  intention waiting),普通索引上(index idxname),物理记录(physical record),间隙区间(未知,+∞);

MySQL死锁如何解决

5)事务1回滚(we roll back transaction 1);

查看日志结果

MySQL死锁如何解决

查看日志可得:

这里面,有些朋友可能有疑惑,

我们接下来一小节详细分析一波,一个一个问题来~

死锁分析

死锁死循环四要素

MySQL死锁如何解决

事务A持有什么锁呢?它又想拿什么样的插入意向排他锁呢?

为了方便记录,例子用W表示Wei,J表示Jay,E表示Eason哈~

我们先来分析事务A中update语句的加锁情况~

update account set balance =1000 where name ='Wei';

间隙锁:

记录锁

Next-Key锁

综上所述,事务A执行完update更新语句,会持有锁:

我们再来分析一波事务A中insert语句的加锁情况

insert into account values(null,'Jay',100);

间隙锁:

插入意向锁(Insert Intention)

因此,事务A的update语句和insert语句执行完,它是持有了 (E,W]的 Next-Key锁,(W,+∞)的Gap锁,想拿到  (E,W)的插入意向排它锁,等待的锁跟死锁日志是对上的,哈哈~

MySQL死锁如何解决

事务B拥有了什么间隙锁?它为什么也要拿插入意向锁?

同理,我们再来分析一波事务B,update语句的加锁分析:

update account set balance =1000 where name ='Eason';

间隙锁:

记录锁

Next-Key锁

综上所述,事务B执行完update更新语句,会持有锁:

我们再来分析一波B中insert语句的加锁情况

insert into account values(null,'Yan',100);

间隙锁:

插入意向锁(Insert Intention)

所以,事务B的update语句和insert语句执行完,它是持有了 (-∞,E]的 Next-Key锁,(E,W)的Gap锁,想拿到  (W,+∞)的间隙锁,即插入意向排它锁,加锁情况跟死锁日志也是对上的~

MySQL死锁如何解决

MySQL死锁如何解决

死锁真相还原

接下来呢,让我们一起还原死锁真相吧~哈哈~

MySQL死锁如何解决

关于MySQL死锁如何解决问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。

推荐阅读:
  1. 解决MySQL死锁的方法
  2. mysql死锁的解决方法

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

mysql

上一篇:MySQL中事务有哪些隔离级别

下一篇:MySQL中怎么解决分类排名问题

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》