Hibernate中处理死锁的正确方法

Voj*_*ěch 8 mysql hibernate transactions

正如这里所述http://dev.mysql.com/doc/refman/5.0/en/innodb-deadlocks.html

...通常情况下,您必须编写应用程序,以便它们始终准备好重新发出事务(如果事务因死锁而回滚)。

此处还注明了/sf/answers/181727101/

如果您使用 InnoDB 或任何行级事务 RDBMS,那么任何写入事务都可能导致死锁,即使在完全正常的情况下也是如此。

从 Hibernate 文档来看,在我看来,它不准备以任何方式处理死锁。在我看来,这些事务以 TransactionRollbackException 结束,然后就完成了。准确地说,我正在使用@Transactional注释。

如果这是真的,所有这些关键系统将永远无法使用 Hibernate。所有这些银行/移动运营商系统如何处理这些问题?

Nor*_*len 6

通过将几乎所有内容包装在事务中并确保创建的代码没有可能出现死锁的代码(真正理解数据模型,编写流程),它们可以防止死锁。InnoDB 使用当前的页面锁定方式很容易陷入死锁。

另外,我认为他们不会将 InnoDB 用于任何重要的代码,因为甚至页面锁上会出现死锁。Oracle 和 MS SQL Server 等大型商业数据库使用页锁和行锁以更精细的方式处理此问题。此外,例如,可以通过控制页面填充和空白百分比来更好地控制 Oracle,这减少了页面锁阻塞事务的机会(不是死锁,只是阻塞一段时间)。

至于hibernate或任何其他代码:一旦遇到死锁情况,代码通常需要改进。这个想法是,您可以控制重新启动事务(在某些情况下会给您带来死锁循环:死锁、重新启动和再次死锁等)。这种代码可能很简单:

  • 发生死锁异常;
  • 仅在初始调用函数中捕获死锁异常;
  • 使用相同的参数再次调用初始调用函数。

InnoDB 具有高事务量,甚至在单记录事务上也会出现死锁。这不应该发生,并且是 MySQL(和衍生品问题)。如果您正在解决这个技术问题,那么这是一个彩票:您可以通过更改 ACID 合规性或通过更改技术/逻辑表布局(例如通过分区)来增加您的机会。分区将减少 DBMS 更新索引所花费的时间,从而缩短事务时间,从而减少由于记录锁升级而发生死锁的机会。将页面大小减小到更小的页面大小也具有类似的效果。但是:它只是减少了机会(因此您会看到更少的死锁,但它们仍然会发生)。

有两种可能的解决方案可以完全避免死锁:

  • 重写代码,让单例处理发生死锁的表。必须对代码进行分区,以便在此表上执行操作的代码位于单个事务中,而所有其他代码位于其他事务中。这会破坏任何事务设计,如果发生另一个问题,回滚事务将是一场噩梦(现在至少有 2 个甚至可能 3 个事务)。
  • 切换 DBMS 引擎:如果您使用 hibernate,这非常简单,只是您必须学习处理这个新 DBMS 的内部结构。

使用 hibernate/Spring/JPA 处理死锁@Transaction

使用单例设计,最好单向流动:

函数 A 调用函数 B、C、D,但除了确认或密钥(如果可能)之外,B、C、D 不会向 A 返回任何数据。

如果发生异常,Hibernate 会回滚缓存上的更改,因此主要担心的是调用函数。只要它们是无状态的,最重要的是事务启动函数A:

某些用户通过操作调用控制器。该动作调用函数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();
   }
}
Run Code Online (Sandbox Code Playgroud)

数据访问层抛出的异常必须在外部捕获,@Transaction以便所有的