我在项目中使用了hibernate模式,并且由于非常简单的数据库操作而获得了随机的表观死锁。
有一个堆栈跟踪:https : //gist.github.com/knyttl/8999006 –让我感到困惑的是,第一个异常是RollbackException,然后是LockAquisition异常。
问题经常发生在类似的条款上:
@Transactional public void setLastActivity() { User user = em.findById(...); user.setLastActivity(new Date()); em.merge(user); em.flush(); }
我很困惑,因为我不知道这是Hibernate,MySQL还是C3P0的问题。
我的Hibernate配置:
<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>
编辑1:
编辑2:
这些方法也会发生这种情况-需要使用@Transactional进行注释的那些方法:
@Transactional public void setLastActivity() { em.insertNative("table") .values(...) .execute(); }
由于死锁发生的频率很高,因此应用程序的某些线程似乎长时间持有锁。
应用程序中的每个线程在访问数据库时都会使用其自己的一个数据库连接/一个连接,因此从数据库的角度来看,两个线程是争夺数据库锁的两个不同的客户端。
如果一个线程长时间持有锁并以一定顺序获取它们,而第二个线程随即获得了相同的锁却以不同的顺序获取,则肯定会发生死锁
读操作中也会发生死锁,这意味着某些线程也正在获取读锁。如果线程在REPEATABLE_READ隔离级别或级别运行事务,则会发生这种情况SERIALIZABLE。
REPEATABLE_READ
SERIALIZABLE
为了解决这个问题,尝试搜索的用途Isolation.REPEATABLE_READ和Isolation.SERIALIZABLE项目,来看看这被使用。
Isolation.REPEATABLE_READ
Isolation.SERIALIZABLE
或者,使用默认READ_COMMITTED隔离级别,并使用注释实体@Version,以使用乐观锁定来处理并发。
READ_COMMITTED
@Version
还要尝试识别长时间运行的事务,有时会发生这种情况,因为@Transactional将它们放置在错误的位置并在批处理示例中包装了整个文件的处理,而不是逐行进行事务。
@Transactional
这是一个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 >
可以通过本机查询或JPQL进行更新查询。
在没有的方法中@Transactional,查询将在其自己的实体管理器中执行,并且仅返回分离的实体,因为在运行查询后会立即关闭会话。
因此,没有这种方法的方法中的延迟初始化异常@Transactional是正常的。您也可以将它们设置@Transactional(readOnly=true)为。
@Transactional(readOnly=true)