这种僵局会如何发生?

Posted

技术标签:

【中文标题】这种僵局会如何发生?【英文标题】:How would this deadlock happens? 【发布时间】:2019-05-26 17:37:17 【问题描述】:

最近在处理一个SQL问题,我有两个事务,事务A先拿到Next-Key Locks,然后事务B尝试获取同一个锁,所以一直在等待,然后事务A尝试获取插入意向锁,死锁就发生了。但我很困惑为什么会发生这种情况?

这是我的表结构:

CREATE TABLE `changeset` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT',    
  `userId` int(10) NOT NULL COMMENT,    
  `documentId` varchar(20) NOT NULL,    
  `memberId` bigint(13) NOT NULL,    
  `createTime` bigint(13) NOT NULL,    
  `version` bigint(13) NOT NULL COMMENT,    
  `changesets` mediumtext,    
  PRIMARY KEY (`id`),    
  UNIQUE KEY `uniq_documentId_version` (`documentId`,`version`) USING BTREE    
) ENGINE=InnoDB AUTO_INCREMENT=85771623 DEFAULT CHARSET=utf8

这是我的死锁日志:

(1) 交易:

TRANSACTION 22640,ACTIVE 66 秒开始索引读取

mysql 表正在使用 1,已锁定 1

LOCK WAIT 2 个锁结构,堆大小 1136,1 行锁

MySQL 线程 id 209,操作系统线程句柄 123145559986176,查询 id 6204 localhost root 发送数据

select * from changeset where documentId = '7oO5C_v' and version >= 13 for update

(1) 等待授予此锁定:

RECORD LOCKS 空间 id 107 页号 15 n 位 704 索引 uniq_documentId_version 表 test.changeset trx id 22640 lock_mode X 等待

记录锁,堆号 2 物理记录:n_fields 3;紧凑的格式; 信息位 0

0: 长度 7;十六进制 3976735431644a; asc 9vsT1dJ;;

1: 长度 8;十六进制 8000000000000000;升序;;

2: 长度 4;十六进制 051cbef7;升序;;

(2) 交易:

事务 22639,正在插入 95 秒

mysql 表正在使用 1,已锁定 1

6 个锁结构,堆大小 1136,4 行锁,撤消日志条目 1

MySQL 线程 id 212,操作系统线程句柄 123145561657344,查询 id 6210 本地主机根更新insert into changeset values (0, 9, '7oO5C_v', 814, 1, 13, 'x')

(2) 持有锁:

RECORD LOCKS 空间 id 107 页号 15 n 位 704 索引 uniq_documentId_version 表 test.changeset trx id 22639 lock_mode X

记录锁,堆号 2 物理记录:n_fields 3;紧凑的格式; 信息位 0

0: 长度 7;十六进制 3976735431644a; asc 9vsT1dJ;;

1: 长度 8;十六进制 8000000000000000;升序;;

2: 长度 4;十六进制 051cbef7;升序;;

(2) 等待授予此锁定:

RECORD LOCKS 空间 id 107 页号 15 n 位 704 索引 uniq_documentId_version 表 test.changeset trx id 22639 lock_mode X 在记录插入意图等待之前锁定间隙

记录锁,堆号 2 物理记录:n_fields 3;紧凑的格式; 信息位 0

0: 长度 7;十六进制 3976735431644a; asc 9vsT1dJ;;

1: 长度 8;十六进制 8000000000000000;升序;;

2: 长度 4;十六进制 051cbef7;升序;;

我们回滚交易 (1)

【问题讨论】:

可能也方便我们查看导致死锁的代码?目前看起来很简单“一个事务已开始读取,而另一个事务正在尝试写入正在读取的同一数据区域”? 隔离级别是多少?从日志看,你有两个长时间运行的事务,事务有你描述的那么简单吗? 【参考方案1】:

你在你的选择中使用过“(nolock)”吗?

Select * From Table with (nolock)

【讨论】:

以上是关于这种僵局会如何发生?的主要内容,如果未能解决你的问题,请参考以下文章

寻找竞争条件的方法

离开范围后类变量会自动删除,我如何防止这种情况发生?

如何使用第三个列表将列表链接到另一个列表?,有时有效,但发生这种情况时会出错

解决僵局的策略?

WPF:一旦我在代码中设置了一个属性,它就会永远忽略 XAML 绑定......我该如何防止这种情况发生?

交易陷入僵局