Java Hibernate:尝试获取锁时发现死锁

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21777205/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 10:36:20  来源:igfitidea点击:

Hibernate: Deadlock found when trying to obtain lock

javamysqlhibernatec3p0

提问by Vojtěch

I am using hibernate in my project and I am getting random Apparent Deadlocks for very simple database operations.

我在我的项目中使用了 hibernate,并且由于非常简单的数据库操作而出现随机的明显死锁。

There is one of the Stack Traces: https://gist.github.com/knyttl/8999006– What confuses me, that the first Exception is RollbackException and then there are LockAquisition Exceptions.

有一个堆栈跟踪:https: //gist.github.com/knyttl/8999006 – 让我困惑的是,第一个异常是 RollbackException,然后是 LockAquisition Exceptions。

The problem happens often on similar clauses:

这个问题经常发生在类似的条款上:

@Transactional
public void setLastActivity() {
    User user = em.findById(...);
    user.setLastActivity(new Date());
    em.merge(user);
    em.flush();
}

I am quite stuck as I don't know whether it is problem of Hibernate, MySQL or C3P0.

我很困惑,因为我不知道是 Hibernate、MySQL 还是 C3P0 的问题。

My Hibernate configuration:

我的休眠配置:

            <prop key="hibernate.dialect">${database.dialect}</prop>
            <prop key="hibernate.hbm2ddl.auto">${database.structure}</prop>
            <prop key="hibernate.connection.url">${database.connection}</prop>
            <prop key="hibernate.connection.username">${database.username}</prop>
            <prop key="hibernate.connection.password">${database.password}</prop>
            <prop key="hibernate.connection.driver_class">${database.driver}</prop>
            <prop key="hibernate.connection.shutdown">true</prop>
            <prop key="hibernate.connection.writedelay">0</prop>
            <prop key="hibernate.connection.characterEncoding">UTF-8</prop>
            <prop key="hibernate.connection.charSet">UTF-8</prop>
            <prop key="hibernate.show_sql">${database.show_sql}</prop>
            <prop key="hibernate.format_sql">false</prop>
            <prop key="hibernate.ejb.metamodel.generation">disabled</prop>
            <!-- Use the C3P0 connection pool provider -->
            <prop key="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</prop>
            <prop key="hibernate.c3p0.min_size">0</prop>
            <prop key="hibernate.c3p0.max_size">50</prop>
            <prop key="hibernate.c3p0.timeout">120</prop>
            <prop key="hibernate.c3p0.max_statements">0</prop>
            <prop key="hibernate.c3p0.max_statementsPerConnection">0</prop>
            <prop key="hibernate.c3p0.maxStatementsPerConnection">0</prop>
            <prop key="hibernate.c3p0.idle_test_period">120</prop>
            <prop key="hibernate.c3p0.acquire_increment">1</prop>
            <prop key="hibernate.c3p0.numHelperThreads">8</prop>

EDIT1:

编辑1:

  • I wrote above there were Apparent Deadlocks happening - that was wrong, only "Deadlock found when trying to obtain lock" happen.
  • 我在上面写道,发生了明显的死锁 - 这是错误的,只有“尝试获取锁时发现死锁”发生。

EDIT2:

编辑2:

This happens also on these methods - those NEEDS to be annotated with @Transactional:

这也会发生在这些方法上——那些需要用@Transactional 注释的方法:

@Transactional
public void setLastActivity() {
    em.insertNative("table")
           .values(...)
           .execute();
}

采纳答案by Angular University

Because the deadlocks happen so frequently, it looks like some of the threads of the application are holding locks for an extended period of time.

由于死锁发生得如此频繁,看起来应用程序的某些线程会长时间持有锁。

Each thread in the application will use it's own database connection/connections while accessing the database, so from the point of view of the database two threads are two distinct clients that compete for database locks.

应用程序中的每个线程在访问数据库时都会使用它自己的数据库连接/连接,因此从数据库的角度来看,两个线程是两个不同的客户端,它们竞争数据库锁。

If a thread holds locks for an extended period of time and acquires them in a certain order, and a second thread comes along acquiring the same locks but on a different order, deadlock is bound to occur (see herefor details on this frequent deadlock cause).

如果一个线程长时间持有锁并以特定顺序获取它们,并且第二个线程以不同的顺序获取相同的锁,则必然会发生死锁(有关此频繁死锁原因的详细信息,请参阅此处)。

Also deadlocks are occurring in read operations, which means that some threads are acquiring read locks as well. This happens if the threads are running transactions in REPEATABLE_READisolation level or SERIALIZABLE.

读操作中也会发生死锁,这意味着一些线程也在获取读锁。如果线程在REPEATABLE_READ隔离级别或SERIALIZABLE.

To solve this, try searching for usages of Isolation.REPEATABLE_READand Isolation.SERIALIZABLEin the project, to see if this is being used.

为了解决这个问题,尝试搜索的用途Isolation.REPEATABLE_READIsolation.SERIALIZABLE项目,来看看这被使用。

As an alternative, use the default READ_COMMITTEDisolation level and annotate the entities with @Version, to handle concurrency using optimistic lockinginstead.

作为替代方案,使用默认READ_COMMITTED隔离级别并使用 , 注释实体以@Version使用乐观锁定处理并发。

Also try to identify long running transactions, this happens sometimes when the @Transactionalis placed at the wrong place and wraps for example the processing of a whole file in the example of a batch processing, instead of doing transactions line by line.

还要尝试识别长时间运行的事务,有时会发生这种情况,因为它@Transactional被放置在错误的位置并且例如在批处理示例中包装整个文件的处理,而不是逐行执行事务。

This a log4j configuration to log the creation/deletion of entity managers and transactions begin/commit/rollback:

这是一个 log4j 配置,用于记录实体管理器的创建/删除和事务的开始/提交/回滚:

   <!-- spring entity manager and transactions -->
<logger name="org.springframework.orm.jpa" additivity ="false">
    <level value="debug" />
    <appender-ref ref="ConsoleAppender" />
</logger >
<logger name="org.springframework.transaction" additivity ="false">
    <level value="debug" />
    <appender-ref ref="ConsoleAppender" />
</logger >
  1. Can I somehow execute update query (either JPA/Native) without having to lock the table via @Transactional?
  1. 我可以以某种方式执行更新查询(JPA/Native)而不必通过@Transactional 锁定表吗?

Update queries are possible via native queries or JPQL.

可以通过本机查询或JPQL进行更新查询。

  1. Can I somehow get into session without using @Transactional?For instance, scheduled thread tries to read Lazy field on Entity yields to LazyInitializationException - no session, if the method is not annotated with @Transactional
  1. 我可以在不使用 @Transactional 的情况下以某种方式进入会话吗?例如,计划线程尝试读取实体上的 Lazy 字段会导致 LazyInitializationException - 没有会话,如果该方法未使用 @Transactional 进行注释

In methods without @Transactional, queries will be executed in it's own entity manager and return only detached entities, as thee session is closed immediatelly after the query is run.

在没有 的方法中@Transactional,查询将在它自己的实体管理器中执行并且只返回分离的实体,因为在查询运行后会话会立即关闭。

so the lazy initialization exceptions in methods without @Transactionalis normal. You can set them to @Transactional(readOnly=true)as well.

所以没有方法中的延迟初始化异常@Transactional是正常的。您也可以将它们设置@Transactional(readOnly=true)为。

回答by Shishir Kumar

This is the error with MySQL.

这是 MySQL 的错误。

The most easy way to resolve & avoid deadlocks is to reorder the DB operations happening in the application.

解决和避免死锁的最简单方法是重新排序应用程序中发生的数据库操作。

Deadlock mostly occurs when more than one resource/connection try to acquire more than one lock at opposite orders, as below:

死锁主要发生在多个资源/连接尝试以相反的顺序获取多个锁时,如下所示:

connection 1: locks key(1), locks key(2);
connection 2: locks key(2), locks key(1);

In the scenario when both the connections execute at the same time, connection 1 will acquire lock on key(1), and connection 2 on key(2). After that both the connections will wait for other to release the lock on the key. This results in deadlock.

在两个连接同时执行的场景中,连接 1 将获得 key(1) 上的锁,而连接 2 将获得 key(2) 上的锁。之后,两个连接都将等待其他连接释放密钥上的锁定。这会导致死锁。

But, a little tweak in the order of the transactions, then deadlocks can be avoided.

但是,稍微调整一下事务的顺序,就可以避免死锁。

connection 1: locks key(1), locks key(2);
connection 2: locks key(1), locks key(2);

Above re-order is deadlock proof.

以上重新排序是死锁证明。

Other ways to avoid deadlocks is to have a transaction management mechanism. Transaction managementby Spring is almost plug-n-play. Moreover, you can have a deadlock retry policy in place. An interesting deadlock retry via Spring AOP can be found here. This way you just need to add the annotation to the method you want to retry in case of deadlock.

避免死锁的其他方法是拥有事务管理机制。Spring 的事务管理几乎是即插即用的。此外,您可以制定死锁重试策略。可以在此处找到通过 Spring AOP 进行的有趣的死锁重试。这样,您只需要将注解添加到您想在死锁情况下重试的方法。

For more debug logs on deadlock to find out which statements are suspicious, try running the "show engine innodb status" diagnostics. Also, you can have a look at How to Cope with Deadlocks.

有关死锁的更多调试日志以找出哪些语句是可疑的,请尝试运行“show engine innodb status”诊断程序。此外,您可以查看如何应对死锁

UPDATE:A scenario for deadlocks in transactional DB operations.

更新:事务性数据库操作中的死锁场景。

In a transactional database, a deadlock happens when two processes each within its own transaction updates two rows of information but in the opposite order. For example, process A updates row 1 then row 2 in the exact time-frame process B updates row 2 then row 1. Process A can't finish updating row 2 until process B is finished, but it cannot finish updating row 1 until process A finishes. No matter how much time is allowed to pass, this situation will never resolve itself and because of this database management systems will typically kill the transaction of the process that has done the least amount of work.

在事务数据库中,当两个进程各自在其自己的事务中更新两行信息但顺序相反时,就会发生死锁。例如,进程 A 在确切的时间范围内更新第 1 行,然后更新第 2 行,进程 B 更新第 2 行,然后更新第 1 行。进程 A 在进程 B 完成之前无法完成对第 2 行的更新,但在进程之前无法完成对第 1 行的更新一个结束。无论允许经过多少时间,这种情况都不会自行解决,因此数据库管理系统通常会终止完成最少工作的进程的事务。

Shishir

石狮