文章插图
在生产环境中如果出现MySQL死锁问题该如何排查和解决呢,本文将模拟真实死锁场景进行排查,最后总结下实际开发中如何尽量避免死锁发生 。
一、准备好相关数据和环境当前自己的数据版本是8.0.22
mysql> select @@version;+-----------+| @@version |+-----------+| 8.0.22|+-----------+1 row in set (0.00 sec)
数据库隔离级别(默认隔离级别)mysql> select @@transaction_isolation;+-------------------------+| @@transaction_isolation |+-------------------------+| REPEATABLE-READ|+-------------------------+1 row in set (0.00 sec)
自动提交关闭mysql> select @@autocommit;+--------------+| @@autocommit |+--------------+|1 |+--------------+1 row in set (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)
表结构这个age为 非唯一的索引,这点对下面整个案例非常重要 。
-- id是自增主键,age是非唯一索引,name普通字段CREATE TABLE `user` (`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',`age` int DEFAULT NULL COMMENT '年龄',`name` varchar(255)DEFAULT NULL COMMENT '姓名',PRIMARY KEY (`id`),KEY `idx_age` (`age`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户信息表';
表中暂时先插入两条数据文章插图
二、模拟出真实死锁案例开启两个终端模拟事务并发情况,执行顺序以及实验现象如下:
1)事务A执行更新操作,更新成功
mysql> updateuserset name = 'wangwu' where age= 20;Query OK, 1 row affected (0.00 sec)
- 事务B执行更新操作,更新成功
mysql> updateuserset name = 'zhaoliu' where age= 10;Query OK, 1 row affected (0.00 sec)
3)事务A执行插入操作,陷入阻塞~mysql> insert into user values (null, 15, "tianqi");
文章插图
4)事务B执行插入操作,插入成功,同时事务A的插入由阻塞变为死锁error 。
insert into user values (null, 30, "wangba");Query OK, 1 row affected (0.00 sec)
事务A的插入操作变成报错 。文章插图
上面四步操作后,我们分别对事务A和事务B进行commit操作 。
mysql> commit;Query OK, 0 rows affected (0.00 sec)
我们再来看数据库中表的数据 。文章插图
我们发现,事务B的所有操作最终都成功了,而事务A的操作因为报错都回滚了 。所以事务A的操作都失败 。
那既然是死锁,为什么回滚事务A,而不是事务B,是随机的还是有机制在里面?
我们可以理解死锁是数据库对事务的保护机制,一旦发生死锁,MySQL会选择相对小的事务(undo较少的)进行回滚 。
三、查看分析死锁日志可以用 show engine innodb status,查看最近一次死锁日志哈,执行后,死锁日志如下(只展示部分日志):
LATEST DETECTED DEADLOCK------------------------2021-12-24 06:02:52 0x7ff7074f8700*** (1) TRANSACTION:TRANSACTION 2554368, ACTIVE 22 sec insertingmysql tables in use 1, locked 1LOCK WAIT 4 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2INSERT INTO user VALUES (NULL, 15, "tianqi")*** (1) HOLDS THE LOCK(S):RECORD LOCKS space id 309 page no 5 n bits 72 index idx_age of table `mall_goods`.`user` trx id 2554368 lock_mode XRecord lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0 0: len 8; hex 73757072656d756d; asc supremum;;*** (1) WAITING FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 309 page no 5 n bits 72 index idx_age of table `mall_goods`.`user` trx id 2554368 lock_mode X locks gap before rec insert intention waitingRecord lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000014; asc;; 1: len 4; hex 80000002; asc;;*** (2) TRANSACTION:TRANSACTION 2554369, ACTIVE 14 sec insertingmysql tables in use 1, locked 1LOCK WAIT 5 lock struct(s), heap size 1136, 4 row lock(s), undo log entries 2INSERT INTO user VALUES (NULL, 30, "wangba")*** (2) HOLDS THE LOCK(S):RECORD LOCKS space id 309 page no 5 n bits 72 index idx_age of table `mall_goods`.`user` trx id 2554369 lock_mode X locks gap before recRecord lock, heap no 4 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 80000014; asc;; 1: len 4; hex 80000002; asc;;*** (2) WAITING FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 309 page no 5 n bits 72 index idx_age of table `mall_goods`.`user` trx id 2554369 lock_mode X insert intention waitingRecord 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)
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 春季养生重点为护肝 肝火上升教你预防
- 手把手教你进行安卓逆向之篡改apk名称和图标
- 补血吃什么好 教你做六款家庭补血食疗方
- 专家教你三款药膳良方 让你与空调病说
- 松树特征介绍及生态特征分析
- 松树用途的介绍与分析
- Spring Cloud Stream使用详解及部分重点源码分析
- 三招教你查看别人在我电脑上操作了些什么,跟着我,看这个就够
- wireshark数据包分析工具
- 最全面的血型和性格分析