Spring 和 Hibernate 的长时间运行事务?

Posted

技术标签:

【中文标题】Spring 和 Hibernate 的长时间运行事务?【英文标题】:Long running transactions with Spring and Hibernate? 【发布时间】:2011-02-26 20:25:27 【问题描述】:

我要解决的根本问题是在 mysql 中运行一个生成多个临时表的任务,这些临时表需要保持足够长的时间才能在创建后从 Java 中获取结果。由于涉及的数据量大,任务必须分批完成。每个批处理都是对通过 JDBC 调用的存储过程的调用。对于大型数据集,整个过程可能需要半小时或更长时间。

为了确保访问临时表,我在一个带有 TransactionCallbackWithoutResult 的 Spring 事务中运行整个任务,从头到尾。否则,我可能会获得一个无法访问临时表的不同连接(在我将所有内容包装到事务中之前偶尔会发生这种情况)。

这在我的开发环境中运行良好。但是,在生产中我遇到了以下异常:

java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction

当我的长时间运行的事务执行期间另一个任务试图访问一些相同的表时,就会发生这种情况。让我感到困惑的是,长时间运行的事务只插入或更新到临时表中。对非临时表的所有访问都只是选择。从我能找到的文档中,默认的 Spring 事务隔离级别不应该导致 MySQL 在这种情况下阻塞。

所以我的第一个问题是,这是正确的方法吗?我能否确保通过 Hibernate 模板反复获得相同的连接而无需长时间运行事务?

如果长期运行的事务方法是正确的,我应该在隔离级别方面检查什么?我的理解是否正确,Spring/MySQL 事务中的默认隔离级别不应锁定仅通过选择访问的表?如何调试导致冲突的表,并防止这些表被事务锁定?

【问题讨论】:

【参考方案1】:

正如 Justin 关于事务超时所提到的,我最近遇到了连接池(在我的例子中是 Tomcat 7 中的 tomcat dbcp)的问题,该设置应该标记长时间运行的连接标记放弃然后关闭它们。调整这些参数后,我可以避免这个问题。

【讨论】:

【参考方案2】:

当您说您的表是临时的时,它是事务范围的吗?这可能会导致其他交易(可能在不同的交易上)无法看到/访问它。也许涉及真实表和临时表的连接会以某种方式锁定真实表。

根本原因:您是否尝试使用 MySQL 工具来确定是什么锁定了连接?它可能类似于下一行锁定。我不太了解 MySQL 工具,但是在 oracle 上您可以看到哪些连接阻塞了其他连接。

事务超时:您应该创建一个超时时间更长的第二个连接池/数据源。将该连接池用于您的长时间运行的任务。我认为您的生产环境正在“尝试”通过检测卡住的连接来帮助您。

【讨论】:

【参考方案3】:

我认为长时间保持交易开放是邪恶的。在我的职业生涯中,“扩展”的定义从几秒下降到毫秒。

这是不可重复的问题和令人头疼的问题的源头。

在这种情况下,我会咬紧牙关,在软件中保留一个“工作日志”,如果批处理失败,您可以反向回放以进行清理。

【讨论】:

你有解决“保持连接”问题的方法吗?在我的例子中,Spring 和 Hibernate 调解了我与数据库的连接,而且我每次都无法获得相同的连接,而无需将所有内容都包装在一个事务中。如果不维护相同的连接,我将无法访问临时表。 如果您保留带有 id 的工作日志以及批处理中达到的里程碑,您可以创建名为 TEMP__STUFF 的常规表。根据达到的里程碑,您可以跳过并继续中止的批处理。处理完成后(即达到里程碑完成)删除表并记录您达到里程碑“已清理”。现在,所有处理都可以通过细粒度的交易来完成。这种方法的不便之处在于,当批处理作业开始时,您看不到数据库状态的“快照”。这可能是也可能不是问题。 这是非常好的建议。但是,我还有一个问题。一些初始处理会创建在每个批次中访问的临时表。此初始处理需要大量时间,如果必须为每个批次重复此操作,这将严重影响性能。我不确定如何处理这个初始处理,但我会多考虑一下。

以上是关于Spring 和 Hibernate 的长时间运行事务?的主要内容,如果未能解决你的问题,请参考以下文章

org.hibernate.exception.GenericJDBCException 的发生:无法为长时间运行的进程打开连接

用于长时间运行过程的 Spring SSE

Hibernate - 第一个 Sql 插入需要很长时间

Spring Boot:在长时间运行的存储过程中检测到明显的连接泄漏

Spring上的DBCP和Hibernate,不会重新打开死连接,为啥?

使用 Spring Cloud DataFlow 在无限运行的应用程序中编排长时间运行的外部批处理作业是不是合适?