Innodb外鍵引發的死鎖案例分析

最近項目中忽然發現一次鎖現象,訂單屢次付款,最後一次退款。退款完成後支付系統手動第三方回調,支付系統屢次通知訂單系統,訂單系統在這個過程當中發生死鎖,下面給出訂單系統表結構作模擬死鎖。html

情景

  • 數據庫結構(5.7.13)
create database test_deadlock default character set utf8 collate utf8_general_ci;

use test_deadlock;

create table db_order(
  id bigint(1) not null auto_increment comment '主鍵',
  order_no varchar(64) not null comment '訂單號',
  order_status tinyint(4) not null default '1' comment '訂單狀態',
  create_date timestamp not null default current_timestamp comment '開單時間',
  primary key (id)
)engine=innodb default charset =utf8;

create table db_payment(
  id bigint not null auto_increment comment '主鍵',
  order_id bigint(1) not null comment '訂單主表id',
  payment_amount decimal(19,2) not null default '0' comment '支付金額',
  primary key (id)
)engine=innodb default charset =utf8;;

alter table db_payment add constraint   fk_order_id foreign key(order_id) references db_order(id);
複製代碼
  • 初始化數據
insert into db_order(order_no,order_status) values ('10001',5);
insert into db_payment(order_id, payment_amount) values (1,100);
複製代碼
  • 第一個事務
start transaction;
  insert into db_payment(order_id, payment_amount) values (1,100);
  update db_order set order_status=6 where id=1;
commit ;
複製代碼
  • 第二個事務
start transaction;
  insert into db_payment(order_id, payment_amount) values (1,200);
  update db_order set order_status=7 where id=1;
commit ;
複製代碼

模擬

爲了方便模擬,這個使用idea鏈接數據庫分別打開兩個console,而且開啓Manual模式。git

  • 事務A

圖2

  • 事務B

圖1

這裏咱們使用TA(1)表示執行第一個事務的第一行代碼。首先咱們執行TA(1)和TA(2),而後執行TB(1),TB(2),而後在執行TA(3),再執行TB(3),此時獲得結構以下。github

[40001][1213] Deadlock found when trying to get lock; try restarting transaction
複製代碼

能夠看出InnoDB檢測到死鎖。sql

接下來咱們刪除外鍵,執行操做數據庫

alter table db_payment drop foreign key fk_order_id;
複製代碼

而後再次執行上面的操做,操做過程分別問TA(1),TA(2),TB(1),TB(2),TA(3),TB(3),TA(4),TB(4),最後兩個事務都完成執行。bash

能夠看出一個有外鍵和一個沒有外鍵的區別。ide

分析緣由

  • 核心知識點 爲了理解上文中死鎖的緣由,必需要理解清楚Innodb的鎖的機制,MySQL鎖的機制文章不少,能夠去官網找文檔或者閱讀他人的博客,這裏給出一篇博客快速瞭解innodb鎖概念MySQL InnoDB自增加鎖和外鍵鎖以便於咱們理解本文中的死鎖問題。ui

  • 分析 idea

    圖3

咱們用這一張圖分析完爲何死鎖,在第5步和第6步的時候發生了相互等待,Innodb在TB中檢查到了死鎖,反過來思考,加入數據庫刪除了外鍵,在第2步我第3步作insert db_payment操做的時候都沒有對db_order id=1的這行數據加入S鎖,那麼就沒有步驟5對步驟4的S鎖等待,顯然這個執行過程只有步驟6對步驟5等待,TAcommit以後,TB就會得到鎖執行下一步commit。spa

總結

使用MySQL開發過程當中須要對鎖的知識理解清楚,否則在業務代碼中就有可能產生死鎖,尤爲是要知道Innodb使用外鍵的時候的鎖機制,才能更好的避免生產環境發生死鎖,形成嚴重bug。

參考

相關文章
相關標籤/搜索