Mysql update後insert造成死鎖原因分析及解決

系統中出現死鎖的日誌如下:

複製代碼

*** (1) TRANSACTION:
TRANSACTION 1331088253, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 7 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 4
MySQL thread id 14699751, OS thread handle 0x7fc5eaeda700, query id 382901670 172.18.140.10 bms update
INSERT INTO `finance_settlement_detail` (`order_detail_id`, `tenant_id`, `store_id`, `order_id`, `working_type_id`, `working_item_id`, `working_item_base_id`, `working_type_name`, `item_code_all`, `achievements_code`, `price_code`, `item_name_all`, `copies_num`, `one_copies_num`, `show_copies_num`, `unit_num`, `unit_price`, `discount`, `amount`, `standard_unit_price`, `standard_total_price`, `item_code1`, `item_name1`, `item_code2`, `item_name2`, `item_code3`, `item_name3`, `item_code4`, `item_name4`, `item_code5`, `item_name5`, `make_info`, `material_id`, `material_name`, `cost_price`, `cost_amount`, `unit`, `make_order_id`, `complete_num`, `outsourcing`, `create_time`, `customer_hash`, `settlement_hash`, `rate`, `product_name`, `below_lowest_price`, `item_lowest_price`, `type_lowest_discount`, `finance_settlement_id`) VALUES (1236472, 125, 1046, 451483, 655, 54047, NULL, '設計/影像', 'custom', NUL
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1156 page no 24892 n bits 760 index `finance_settlement_id` of table `test`.`finance_settlement_detail` trx id 1331088253 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;;
 
 *** (2) TRANSACTION:
TRANSACTION 1331088264, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
7 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 4
MySQL thread id 14699754, OS thread handle 0x7fc5eacd2700, query id 382901673 172.18.140.10 bms update
INSERT INTO `finance_settlement_detail` (`order_detail_id`, `tenant_id`, `store_id`, `order_id`, `working_type_id`, `working_item_id`, `working_item_base_id`, `working_type_name`, `item_code_all`, `achievements_code`, `price_code`, `item_name_all`, `copies_num`, `one_copies_num`, `show_copies_num`, `unit_num`, `unit_price`, `discount`, `amount`, `standard_unit_price`, `standard_total_price`, `item_code1`, `item_name1`, `item_code2`, `item_name2`, `item_code3`, `item_name3`, `item_code4`, `item_name4`, `item_code5`, `item_name5`, `make_info`, `material_id`, `material_name`, `cost_price`, `cost_amount`, `unit`, `make_order_id`, `complete_num`, `outsourcing`, `create_time`, `customer_hash`, `settlement_hash`, `rate`, `product_name`, `below_lowest_price`, `item_lowest_price`, `type_lowest_discount`, `finance_settlement_id`) VALUES (1247931, 118, 240, 455597, 961, 115484, 40698, '彩色快印', 'csdy a4dm 80
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 1156 page no 24892 n bits 760 index `finance_settlement_id` of table `test`.`finance_settlement_detail` trx id 1331088264 lock_mode X
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;
 
 *** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1156 page no 24892 n bits 760 index `finance_settlement_id` of table `test`.`finance_settlement_detail` trx id 1331088264 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 (2)
2018-10-26 12:21:54 7fc5eaf5c700InnoDB: transactions deadlock detected, dumping detailed information.
2018-10-26 12:21:54 7fc5eaf5c700

複製代碼

死鎖日誌分析:

1、事務1執行insert語句等待獲得X鎖;

2、事務2現持有S鎖,但執行insert語句也在等待X鎖,這樣就存在兩個事務間相互等待,死鎖產生,Mysql自動回滾了事務2;

3、表引擎爲innodb,行鎖,在字段finance_settlement_id形成,普通索引而非主鍵索引;

4、因爲Mysql死鎖日誌打印不完全,無法知道上文死鎖產生前的sql語句的執行情況,根據以上還無法完全分析出死鎖產生的根本原因。

 

根據使用Xdebug工具進一步調試跟蹤代碼執行情況,發現在死鎖前有update finance_settlement_detail where finance_settlement_Id = XXX這樣的語句,這應該就是死鎖產生的”兇手“了。

最終原因分析:

1、innodb引擎下update在默認情況下是行鎖,但是在Mysql默認隔離級別(可重複讀)下,一旦update更新的數據行不存在,則會產生間隙鎖(Gap lock);

2、事務1 update不存在的數據行,產生了Gap lock,事務2 update不存在的數據行,也產生了Gap lock;

3、事務1 insert操作需要等待對方釋放X鎖,事務2 insert操作也需要等待對方釋放X鎖,死鎖產生,Mysql自動回滾了事務2;

 

如何解決死鎖:

解決死鎖的原則就是破壞死鎖產生的條件,在以上案例中,只需要判斷當對應數據行不存在時,不執行update語句即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章