主键会导致mysql死锁吗?
我最近遇到了一个奇怪的mysql死锁,我的表看起来像(为了简单起见,我删除了不相关的列): 当我的节点关闭时,它将删除节点表中的记录。此时,作业队列可能正在删除作业队列表中具有Node.nodeId外键的队列。然后mysql抛出一个异常: 原因:com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:尝试获取锁时发现死锁;尝试重新启动事务 我检查了数据库,JobQueue已成功删除,但节点未删除。我知道外键的顺序可能会导致死锁,但在我的例子中,节点表并没有外键,只有主键。那么僵局怎么会发生呢 顺便说一句:我很确定死锁是由JobQueue引起的,我花了很多时间来缩小这个问题,所以在我的测试中只会使用这两个表 更新:主键会导致mysql死锁吗?,mysql,sql,deadlock,Mysql,Sql,Deadlock,我最近遇到了一个奇怪的mysql死锁,我的表看起来像(为了简单起见,我删除了不相关的列): 当我的节点关闭时,它将删除节点表中的记录。此时,作业队列可能正在删除作业队列表中具有Node.nodeId外键的队列。然后mysql抛出一个异常: 原因:com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:尝试获取锁时发现死锁;尝试重新启动事务 我检查了数据库,JobQueue已成功删除,但节点未删除。我知道外键的顺序可能会导
CREATE TABLE JobQueueEntry (
`id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`jobQueueId` bigint unsigned NOT NULL,
`issuerManagementNodeId` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fkJbqEtryMgmtNodeId` FOREIGN KEY (`issuerManagementNodeId`) REFERENCES `Node` (`nodeId`) ON DELETE SET NULL,
CONSTRAINT `fkJobQueueId` FOREIGN KEY (`jobQueueId`) REFERENCES `JobQueue` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
我终于注意到它仍然是由外键顺序引起的问题。实际上还有另一个表JobQueueEntry,它以相反的顺序同时具有指向节点和JobQueue的外键。因此,删除节点时,它会尝试更新JobQueue和JobQueueEntry。发生死锁的原因是JobQueueEntry在节点之前具有JobQueue的外键
谢谢@ctrl的回答 首先,这应该是一个评论,但我现在没有足够的代表,所以。。。我的“评论”基于我的Oracle经验,但我认为这是一个常见的问题,mysql也可以以同样的方式运行 由于您有一个fk on delete集合null,当您从节点中删除某些内容时,db引擎必须通过JobQueue对其进行更新,并且它可能会获取一个表锁来执行此操作(oracle在您的情况下会这样做)。如果您有多个参与者,一些更新/删除作业表和一些更新/删除作业表,则可能会导致死锁 在Oracle中,为了解决这个问题(并获得更好的性能),通常需要在子表的fk列上创建一个索引,例如workerManagementNodeId
如果mysql以另一种更智能的方式实现这一点,请原谅:)当您的系统处于负载状态时,请查看
SHOW PROCESSLIST
。您可能会看到许多积压的查询。@tadman SHOw PROCESSLIST似乎正常,连接数与连接池大小匹配。积压查询是什么意思?在高负载下,MySQL有时会以“死锁”类型的消息导致查询失败,因为它无法获得表上的锁来插入或更新数据。这只是一个猜测,因为您的模式似乎足够简单。不过,更新索引可能是一项代价高昂的操作,因此在发生这种情况时,最好密切监视服务器的行为,以便更好地了解发生这种情况的条件。你能可靠地复制它吗?@tadman是的,系统负载很重,我启动1000个线程并行执行作业是的,我的测试放置了一个重节点,其中1000个线程并行执行作业。在Job表上有很多更新,Job表也有JobQueue表的外键。但索引如何解决这个问题呢?这仅仅是为了提高性能以减少死锁发生的可能性吗?它不仅仅是一个性能修复,它实际上改变了子表的锁定方式(行锁而不是全表锁)并防止死锁。你可以在这里找到一个解释。
CREATE TABLE JobQueueEntry (
`id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`jobQueueId` bigint unsigned NOT NULL,
`issuerManagementNodeId` varchar(32) DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fkJbqEtryMgmtNodeId` FOREIGN KEY (`issuerManagementNodeId`) REFERENCES `Node` (`nodeId`) ON DELETE SET NULL,
CONSTRAINT `fkJobQueueId` FOREIGN KEY (`jobQueueId`) REFERENCES `JobQueue` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;