最近项目中忽然发现一次锁现象,订单屡次付款,最后一次退款。退款完成后支付系统手动第三方回调,支付系统屡次通知订单系统,订单系统在这个过程当中发生死锁,下面给出订单系统表结构作模拟死锁。html
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
这里咱们使用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
咱们用这一张图分析完为何死锁,在第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。