如此处所述http://dev.mysql.com/doc/refman/5.0/en/innodb-deadlocks.html :
... Normally, you must write your applications so that they are always prepared to re-issue a transaction if it gets rolled back because of a deadlock.
这里也注明了https://stackoverflow.com/a/2596101/922584 :
If you are using InnoDB or any row-level transactional RDBMS, then it is possible that any write transaction can cause a deadlock, even in perfectly normal situations.
在我看来,从 Hibernate 文档来看,它不准备以任何方式处理死锁。在我看来,这些交易以 TransactionRollbackException 结束,然后就完成了。准确地说,我使用的是 @Transactional
注释。
如果这是真的,那么所有这些关键系统将永远无法使用 Hibernate。所有这些银行/移动运营商系统如何处理这些问题?
最佳答案
通过将几乎所有内容都包装在事务中并注意创建的代码没有死锁可能的代码(真正理解数据模型,写出流程),它们可以防止死锁。 InnoDB 使用当前的页面锁定方式很容易死锁。
此外,我认为他们不会将 InnoDB 用于任何严肃的代码,因为这甚至会导致页锁死锁。 Oracle 和 MS SQL Server 等较大的商业数据库以更细粒度的方式使用页锁和行锁来处理此问题。此外,例如可以通过控制页面填充和空百分比来更好地控制 Oracle,这减少了页面锁定阻塞事务的机会(不是死锁,只是阻塞了一段时间)。
至于hibernate
或任何其他代码:一旦遇到死锁情况,代码通常需要改进。这个想法是你可以控制重新启动事务(在某些情况下给你一个死锁循环:死锁,重新启动和再次死锁等)。这种代码可能很简单:
- 发生死锁异常;
- 只在初始调用函数中捕获死锁异常;
- 使用相同的参数再次调用初始调用函数。
具有高事务量的 InnoDB 即使在单个记录事务上也会出现死锁。这不应该发生并且是一个 MySQL(和衍生问题)。如果您正在解决这个技术问题,那就是彩票:您可以通过更改 ACID 合规性或通过更改技术/逻辑表布局(例如通过分区)来增加您的机会。分区将减少 DBMS 更新索引所花费的时间,从而缩短事务时间,从而减少由于记录锁升级而造成死锁的机会。将页面大小减小到更小的页面大小具有类似的效果。但是:它只是减少了机会(因此您看到的死锁较少,但它们仍然会发生)。
有两种可能的解决方案可以完全避免死锁:
- 重写代码,使单例处理发生死锁的表。必须对代码进行分区,以便在该表上执行操作的代码位于单个事务中,而所有其他代码位于其他事务中。这会破坏任何事务设计,如果出现另一个问题,回滚事务(现在至少有 2 个甚至可能是 3 个事务)将是一场噩梦。
- 切换 DBMS 引擎:如果您使用 Hibernate,这非常简单,只是您必须学习处理这个新 DBMS 的内部结构。
使用 hibernate/Spring/JPA @Transaction
处理死锁:
使用单例设计,最好是单向流:
Function A calls Function B,C,D however B,C,D are not returning any data to A except a confirmation or a key (if possible).
Hibernate 会在异常情况下回滚缓存中的更改,因此主要担心的是调用函数。只要是无状态的,最重要的是事务启动函数A:
Some user calls the controller with action. This action calls function A:
public String someControllerAction(...) {
try {
... some work, should be minimal else we have to undo this in the catch
saveMyData(...);
} catch(TransactionException exp) {
... undo some work
/* This is a loop, so it can get stuck. You can keep a loop
counter or another check to prevent getting stuck forever.
You can also throw this back to the user with a **please retry
button** */
someControllerAction(...);
}
// Starting transaction so that it can be restarted.
@Transactional
private saveMyData(...) throws TransactionException {
try {
... some work
} catch(TransactionException exp) {
... some roll back work if required
Throw new TransactionException();
}
}
数据访问层抛出的异常必须在@Transaction
之外捕获,这样所有的
关于mysql - 在 Hibernate 中处理死锁的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34551005/