mysql> select @@innodb_deadlock_detect; +--------------------------+ | @@innodb_deadlock_detect | +--------------------------+ | 1 | +--------------------------+ 1 row in set (0.00 sec)高并发场景下可以考虑关闭死锁检测,原因是如果锁等待队列很长,死锁检测成本高,会导致实例性能下降。但是前提是应用层面可以避免死锁,因此通常不建议关闭。下面通过介绍一个死锁案例对死锁日志的格式与分析方法有一个感性认识。
------------------------ LATEST DETECTED DEADLOCK ------------------------ 2024-04-14 08:07:05 0x7fb6d39a6700 *** (1) TRANSACTION: TRANSACTION 13020605130, ACTIVE 25 sec starting index read mysql tables in use 1, locked 1 LOCK WAIT 33 lock struct(s), heap size 3520, 33 row lock(s), undo log entries 34 MySQL thread id 2343498932, OS thread handle 140424015394560, query id 28769967039 x.x.x.x xwms_rw updating UPDATE stock_occupy SET update_time = NOW() ,update_user = 'WAPS' ,qty_out_occupy=qty_out_occupy + 12.0000 WHERE map_area_id = 608 AND goods_no='EMG4418433215231' AND owner_no='0' AND lot_no='-1' AND product_level='100' AND org_no = '10' AND distribute_no = '10' AND warehouse_no = '126' AND map_area_id = 608 *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 127 page no 5255 n bits 272 index idx_map_goods_product_lot_owner of table `xwms`.`stock_occupy` trx id 13020605130 lock_mode X locks rec but not gap waiting Record lock, heap no 53 PHYSICAL RECORD: n_fields 6; compact format; info bits 0 0: len 8; hex 8000000000000260; asc `;; 1: len 16; hex 454d4734343138343333323135323331; asc EMG4418433215231;; 2: len 3; hex 313030; asc 100;; 3: len 2; hex 2d31; asc -1;; 4: len 1; hex 30; asc 0;; 5: len 8; hex 8000000000042de4; asc - ;; *** (2) TRANSACTION: TRANSACTION 13020606128, ACTIVE 10 sec starting index read mysql tables in use 1, locked 1 10 lock struct(s), heap size 1136, 7 row lock(s), undo log entries 8 MySQL thread id 2343006037, OS thread handle 140423210886912, query id 28769967052 x.x.x.x xwms_rw updating UPDATE stock_occupy SET update_time = NOW() ,update_user = 'WAPS' ,qty_out_occupy=qty_out_occupy + 11.0000 WHERE map_area_id = 608 AND goods_no='EMG4418442253742' AND owner_no='0' AND lot_no='-1' AND product_level='100' AND org_no = '10' AND distribute_no = '10' AND warehouse_no = '126' AND map_area_id = 608 *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 127 page no 5255 n bits 272 index idx_map_goods_product_lot_owner of table `xwms`.`stock_occupy` trx id 13020606128 lock_mode X locks rec but not gap Record lock, heap no 53 PHYSICAL RECORD: n_fields 6; compact format; info bits 0 0: len 8; hex 8000000000000260; asc `;; 1: len 16; hex 454d4734343138343333323135323331; asc EMG4418433215231;; 2: len 3; hex 313030; asc 100;; 3: len 2; hex 2d31; asc -1;; 4: len 1; hex 30; asc 0;; 5: len 8; hex 8000000000042de4; asc - ;; *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 127 page no 5276 n bits 240 index idx_map_goods_product_lot_owner of table `xwms`.`stock_occupy` trx id 13020606128 lock_mode X locks rec but not gap waiting Record lock, heap no 38 PHYSICAL RECORD: n_fields 6; compact format; info bits 0 0: len 8; hex 8000000000000260; asc `;; 1: len 16; hex 454d4734343138343432323533373432; asc EMG4418442253742;; 2: len 3; hex 313030; asc 100;; 3: len 2; hex 2d31; asc -1;; 4: len 1; hex 30; asc 0;; 5: len 8; hex 8000000000044335; asc C5;; *** WE ROLL BACK TRANSACTION (2)其中:
.binlog 中显示提交事务也就是事务 1 中先后 update 279349 与 273892,因此判断死锁原因是交叉更新。
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id', `map_area_id` bigint(20) NOT NULL COMMENT '地图区域ID', `goods_no` varchar(50) NOT NULL COMMENT '商品编号', `product_level` varchar(50) NOT NULL COMMENT '商品等级', `lot_no` varchar(50) NOT NULL COMMENT '批次号', `owner_no` varchar(50) NOT NULL COMMENT '货主编号', PRIMARY KEY (`id`), UNIQUE KEY `idx_map_goods_product_lot_owner` (`map_area_id`,`goods_no`,`product_level`,`lot_no`,`owner_no`)其中:
InnoDB: *** (1) TRANSACTION: InnoDB: *** (1) WAITING FOR THIS LOCK TO BE GRANTED: InnoDB: *** (2) TRANSACTION: InnoDB: *** (2) HOLDS THE LOCK(S): InnoDB: *** (2) WAITING FOR THIS LOCK TO BE GRANTED: InnoDB: *** WE ROLL BACK TRANSACTION (1)其中主要信息包括:
.SQL 超长时自动截断;
general log,可以获取提交事务与回滚事务中已执行的 SQL,包括已执行无更新的操作,比如删除不存在的记录。
lock_mode = X lock_type = RECORD rec_lock_type = record lock lock_status = WAITING注意:锁的状态分两种,包括已获取到(GRANTED)与等待中(WAITING)。
锁类型 | record | gap | next-key | insert intention |
---|---|---|---|---|
record | ❌ |
|
❌ |
|
gap |
|
|
|
|
next-key | ❌ |
|
❌ |
|
insert intention |
|
❌ | ❌ |
|
如果已有的锁是等待状态,要加的锁与该锁冲突,要加的锁同样会发生锁等待。
insert duplcate / replace,X 型锁。
数据库版本升级,比如 5.7.26 中删除 insert duplicate / replace 唯一键不冲突时的加锁 gap lock;