如何在并发事务中避免数据库死锁?

4
情境简述:
交易A开始...
START TRANSACTION;
UPDATE table_name SET column_name=column_name+1 WHERE id = 1 LIMIT 1;

同时,事务B开始...
START TRANSACTION;
UPDATE table_name SET column_name=column_name+1 WHERE id = 2 LIMIT 1;
UPDATE table_name SET column_name=column_name-1 WHERE id = 1 LIMIT 1;
COMMIT;

目前,事务B正在等待事务A锁定的行1。

而事务A则继续执行...

UPDATE table_name SET column_name=column_name-1 WHERE id = 2 LIMIT 1;
COMMIT;

现在我们遇到了死锁,所以两个事务都在等待对方解锁他们想要更新的行 :'(

正如我在标题中提到的,我们如何在RDBMS事务中防止死锁?

我认为唯一的解决方法是回滚事务B并重新执行它。但是我们如何发现自己处于死锁状态,并立即摆脱它,以及如何保证我们不会陷入无限循环(例如,在非常繁重的Web应用程序中)。

如果需要,我正在使用MySQL。但欢迎为其他RDBMS提供任何解决方案,以帮助从Google来到这里的其他人 :)


你无法完全防止死锁的发生,但可以编写代码来最小化它们的出现。 - Shadow
1
@trincot 我认为是这样的,事务A锁定了第1行,事务B锁定了第2行并等待事务A解锁第1行,而事务A则等待事务B解锁第2行,因此两个事务都在互相等待。如果我错了,请纠正我。谢谢。 - user5483434
1
@Shadow,您能告诉我如何将它们的出现减至最少吗? - user5483434
你可以写一本关于那个的书。你的问题太过宽泛,包括这么多的关系型数据库也没有帮助。我投票关闭这个问题。 - Shadow
MySQL特定问题 - 根据表的类型,您可以使用适当的事务隔离级别同时运行两个事务。我的猜测是您的表类型是ISAM,并且存在表级锁,事务B正在等待事务A完成,以便B可以开始执行。 - SagaciousLearner
显示剩余2条评论
1个回答

5
大多数数据库(如果不是全部)都会自动检测死锁,选择一个会话作为受害者,并自动回滚该会话的事务以打破死锁。例如,这里是MySQL死锁检测和回滚文档。
死锁是编程错误。避免死锁的一个简单解决方案是确保您始终按特定顺序锁定行。例如,如果您有一个事务想要更新两个不同的行,请始终先更新具有较小id的行,然后再更新具有较大id的行。如果您的代码总是这样做,至少就不会有行级死锁。除此之外,在您的代码中实现适当的串行化以进行关键部分。具体而言,这取决于您的应用程序。

谢谢Justin。实际上我在这个问题中听说了死锁(几小时前我问的)。你能帮我解决行级死锁吗?InnoDB中还有其他死锁吗?(表级别?其他的?)此外,英语不是我的母语,请解释一下“在您的代码中为关键部分实现适当的序列化”的含义好吗?我现在会阅读链接网址。再次感谢。 - user5483434
@user5483434 - 绝大多数死锁是行级死锁,但您可能会锁定其他任意数量的资源,这可能导致死锁。如果您搜索“并发编程死锁”,则有(许多)关于并发编程和实现适当序列化方法的书籍。这不是在这种论坛中可以真正涵盖的内容。就像我说的那样,它所涉及的具体内容严重取决于您的应用程序。 - Justin Cave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接