记一次MySQL innodb引擎死锁问题

      最近,在排查项目中服务端的报错时,发现服务端日志有死锁报错(偶发性)。

      查看了具体的报错模块后,梳理出模拟表如下:

CREATE TABLE `test_a` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;



CREATE TABLE `test_b` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `aid` int(11) DEFAULT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_aid` (`aid`),
  CONSTRAINT `fk_aid` FOREIGN KEY (`aid`) REFERENCES `test_a` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;



CREATE TABLE `test_c` (
  `name` varchar(255) NOT NULL,
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `aid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_test_c_aid` (`aid`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
DROP TRIGGER IF EXISTS `test`;
DELIMITER ;;
CREATE TRIGGER `test` AFTER INSERT ON `test_c` FOR EACH ROW INSERT INTO `demo`.`test` (`price`, `realPrice`, `isDeleted`, `createdDate`) VALUES ('0.00', '000000.00', '0', '2018-06-18 13:48:46')
;;
DELIMITER ;

         表关系为,test_a与test_b表有外键关联关系,test_c表独立,但是它有一个触发器(也不会对另外两张表加锁)。

         做如下测试:

-- 会话框1:

SET AUTOCOMMIT = 0;

-- 插入语句1
INSERT INTO test_b (`name`, aid) VALUES ('1111', 1); -- S
-- 更细语句1
UPDATE test_a SET `name` = '112' WHERE id = 1;	-- X

COMMIT;


-- 会话框2:
SET AUTOCOMMIT = 0;

-- 插入语句2
INSERT INTO test_c (`name`, aid) VALUES ('1111', 1); -- trigger
-- 插入语句2
UPDATE test_a SET `name` = '112' WHERE id = 1;	-- X

COMMIT;

    执行顺序:

    插入语句1->插入语句2-> 更新语句2->更新语句1

    执行结果:

    会话1报deadlock,会话2可正常提交;

    执行顺序:

    插入语句1-> 更新语句2->更新语句1

    执行结果:

    会话2报deadlock,会话1可正常提交;

 

    如果取消test_c表的触发器,建立test_c与test_a的外键关系:

    执行顺序:

    插入语句1->插入语句2-> 更新语句2->更新语句1

    执行结果:

    会话1报deadlock,会话2可正常提交;

 

     以上执行结果,表明除了共享锁(S)、排它锁(X)之外,有时候触发器会影响两个事物的commit和rollback结果。

     因此,在给数据库表加外键时应该慎重考虑,因为表在做一些操作的时候会对外键关联表的索引数据加S锁,可能会引起意想不到的死锁报错。且尽量避免在同一事务中对两张有关联的表同时操作(或者保证X锁的操作在S锁操作之前执行),否则高并发情况下容易导致死锁问题。

     加一些拓展:

参考mysql5.6官方文档(我测试使用的mysql版本5.6):

文中提到:当启用死锁检测时(默认),InnoDB会自动检测事务死锁并回滚一个或多个事务来打破死锁。InnoDB会尝试选择小型事务进行回滚,其中事务的大小由插入,更新或删除的行数决定。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值