2016-02-21 66 views
2

场景:如何防止并发事务中的数据库死锁?

交易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正在等待第1行,该行在交易A中被锁定。

和事务A继续...

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

现在,我们有一个死锁,所以交易双方都在等待对方要解锁的行,他们要更新:“(

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

我认为解决这种情况的唯一方法是我们回滚事务B并重新执行它。但是,我们如何才能发现我们处于僵局,立即摆脱困境,我们如何保证我们不会陷入无限循环(例如,在非常沉重的Web应用程序中)。

如果有必要,我使用MySQL。但是对于其他RDBMS的任何解决方案都是值得欢迎的 - 帮助其他人从谷歌来到这里:)

+0

您无法防止死锁,但是,您可以编写最小化其发生的代码。 – Shadow

+1

@trincot我认为,事务A锁定行#1,事务B锁定行#2并等待事务A解锁行#1,并且事务A等待事务B解锁行#2,因此两个事务都在等待对于彼此。如果我错了,你能纠正我吗?谢谢。 – user5483434

+1

@Shadow你能告诉我怎样才能尽量减少他们的发生? – user5483434

回答

2

大多数数据库(如果不是全部的话)会自动检测到死锁,选择一个会话作为受害者,并自动回滚会话交易打破僵局。例如,这里是MySQL deadlock detection and rollback文档。

死锁是编程错误。避免死锁的一个简单方法是确保您始终按特定顺序锁定行。例如,如果您有一个要更新两个不同行的交易,请始终更新第一行较小的id行和较大的id秒行。如果你的代码总是这样做,你至少不会有行级死锁。除此之外,为代码中的关键部分实现适当的序列化。究竟是什么,非常依赖于你的应用程序。

+0

谢谢Justin。其实我听到死锁[这个问题](http://stackoverflow.com/questions/35525208/is-it-ok-to-run-whole-php-application-in-a-mysql-transaction?noredirect=1 #comment58747603_35525208)(我几个小时前问过)。你能帮我解决行级的死锁吗? InnoDB中是否还有其他的死锁? (表级别?任何其他?)另外,英语不是我的母语,请你解释一下你的意思吗?请在你的代码中实现关键部分的适当序列化?我现在会阅读链接的网址。再次感谢。 – user5483434

+0

@ user5483434 - 绝大多数死锁都是行级死锁,但还有其他数量的资源可能会导致死锁。如果您搜索“并发编程死锁”,那么有很多关于并发编程的书籍和实现适当序列化的方法。这不是真的可以在这类论坛中涵盖的东西。就像我说的,它的确切含义很大程度上取决于你的应用。 –