如何解释这种僵局的原因?

时间:2019-07-05 01:22:07

标签: mysql deadlock

有两个事务,事务1在行上持有S锁,事务2要更新该行,然后事务2等待,然后事务1在该行上也执行更新,这时发生死锁,我想知道是什么原因吗?这里的锁情况如何?

我在mysql5.6版本上进行了以下测试。有一个死锁。

表结构:

</div>
<!-- Mask & flexbox options-->

初始数据:

CREATE TABLE `test` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增',
  `uni_id` bigint(20) DEFAULT NULL,
  `current_status` int(11) DEFAULT '0' ,
  `total` int(11) NOT NULL DEFAULT '0' ,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uni_id_unique` (`uni_id`),
  KEY `uni_id_idx` (`uni_id`),
  KEY `current_status_idx` (`current_status`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

按顺序执行以下操作: 1.第一步   交易1:

INSERT INTO `test`(`id`, `uni_id`, `current_status`, `total`) VALUES (1, 1, 0, 1);
  1. 第二步
 start transaction;
 select * from test where id=1 lock in share mode;
  1. 第三步 交易1:
start transaction;
update test set uni_id=1,total=total+1 where uni_id=1;

然后那只脱发发生了。

  1. 第一步:事务1持有S锁。
  2. 第二步:事务2等待,并且根据源代码调试的结果,获得的锁失败。
  3. 第三步:僵局

死锁信息:

update test set current_status=1 where id=1 and 
current_status=0;

2 个答案:

答案 0 :(得分:1)

我认为您对实际发生的情况的分析是完全正确的。这是可能的事件版本:

  1. 第一笔交易在记录上获得S锁
  2. 第二个事务希望在同一记录上获得排他锁,但不能,因为第一个事务持有S锁。因此,该事务等待,试图获取锁。
  3. 第三笔交易也进入同一记录的等待状态,但是现在发生了死锁。

从MySQL documentation

  

在这里,FOR SHARE不是一个好的解决方案,因为如果两个用户同时读取计数器,则其中至少有一个在尝试更新计数器时会陷入死锁状态。

该文档建议,一种更好的方法可能是执行SELECT ... FOR UPDATE

SELECT * FROM test WHERE id = 1 FOR UPDATE;
UPDATE test SET uni_id = 1, total = total+1 WHERE uni_id = 1;

答案 1 :(得分:0)

我的一个朋友解释了这种情况。

从MYSQL文档:

  

此处发生死锁,因为客户端A需要X锁才能删除该行。但是,不能授予该锁定请求,因为客户端B已经具有X锁定请求,并且正在等待客户端A释放其S锁定。由于B事先要求X锁,因此A持有的S锁也不能升级为X锁。结果,InnoDB为其中一个客户端生成错误并释放其锁