在MySQL中获得死锁

时间:2010-05-26 23:25:57

标签: sql mysql database deadlock database-deadlocks

我们非常令人沮丧地在MySQL中遇到死锁。这不是因为超时锁定超时,因为死锁会在它们发生时立即发生。这是在2个独立线程上执行的SQL代码(与连接池有2个独立的连接),这会产生死锁:

UPDATE Sequences SET Counter = LAST_INSERT_ID(Counter + 1) WHERE Sequence IS NULL

序列表有2列:序列和计数器

LAST_INSERT_ID允许我们retrieve this updated counter value as per MySQL's recommendation。这对我们来说非常合适,但我们会遇到这些僵局!为什么我们得到它们?我们如何避免它们?

非常感谢您对此的任何帮助。

编辑:这都是在一个事务中(因为我使用的是Hibernate所必需的),而AUTO_INCREMENT在这里没有意义。我应该更清楚。 Sequences表包含许多序列(在我们的例子中大约有1亿个序列)。我需要增加一个计数器并检索该值。 AUTO_INCREMENT在所有这些中都不起作用,这与Ids或PRIMARY KEYs无关。

4 个答案:

答案 0 :(得分:2)

在事务中包装sql语句。如果您没有使用交易,您将在LAST_INSERT_ID上获得竞争条件。

但实际上,你应该有计数字段auto_increment,所以你让mysql处理这个。

您的第三个解决方案是使用LOCK_TABLES来锁定序列表,以便其他进程无法同时访问它。除非您使用INNODB,否则这可能是最慢的解决方案。

答案 1 :(得分:0)

死锁是任何事务数据库的正常部分,可以随时发生。通常,您应该编写应用程序代码来处理它们,因为没有必要的方法来保证您永远不会遇到死锁。话虽如此,有些情况会增加发生死锁的可能性,例如使用大型事务,并且您可以采取一些措施来减轻它们的发生。

首先,您应该阅读this manual page以更好地了解如何避免它们。

其次,如果你正在做的只是更新一个计数器,那么你真的应该真的使用Counter的AUTO_INCREMENT列,而不是依赖于“select then update”进程,就像你拥有的那样看到的是一种可以产生死锁的竞争条件。基本上,表列的AUTO_INCREMENT属性将作为您的计数器。

最后,我将假设您在事务中有更新语句,因为这会产生频繁的死锁。如果您想看到它的实际效果,请尝试experiment listed here。这正是您的代码正在发生的事情......两个线程试图在提交其中一个之前同时更新相同的记录。即时僵局。

您最好的解决方案是在没有交易的情况下弄清楚如何做到这一点,而AUTO_INCREMENT将允许您这样做。

答案 2 :(得分:0)

没有其他SQL涉及?对我来说似乎不太可能。

'where sequence为null'可能导致全表扫描,导致在每一行/页/ ...上获取读锁。

如果(您的特定引擎不使用MVCC并且)在同一事务中的更新之前存在INSERT,则会成为问题。 INSERT将获得对某些资源(行/页面/ ...)的独占锁定,这将导致任何其他线程获取读锁定等待。因此,两个连接可以首先进行插入,导致它们中的每一个都在表的某个小部分上具有独占锁定,然后它们都尝试进行更新,要求它们中的每一个都能够获取读取锁定。整个桌子。

答案 3 :(得分:0)

我设法使用 MyISAM 表为序列做到了这一点。

然后我有一个名为 getNextCounter 的函数,它执行以下操作:

  • 执行 SELECT sequence_value FROM 序列 where sequence_name = 'test';
  • 执行更新:更新序列 SET sequence_value = LAST_INSERT_ID(last_retrieved_value + 1) WHERE sequence_name = 'test' and sequence_value = 最后检索到的值;
  • 循环重复直到两个查询都成功,然后检索最后一个插入 id。

由于它是一个 MyISAM 表,它不会成为您事务的一部分,因此操作不会导致任何死锁。