我们有一个Java工作流应用程序,它使用Oracle数据库来跟踪其步骤以及与其他服务的交互。在工作流运行期间,执行几次插入/更新/选择,有时候select不会返回更新的数据,即使在成功完成之前运行的插入/更新提交也是如此。在工作流出错后(由于数据不良),如果我们返回并通过第三方应用程序检查数据库,则会显示新的/更新的数据。在我们的提交通过和可见之间似乎存在滞后。这种情况大约占所有工作流运行的2%,并且在数据库使用量很大时会增加。
我们的数据库支持团队建议将参数max-commit-propagation-delay更改为0,因为它默认为700.这似乎是一种可能的解决方案,但最终没有解决我们的问题。
应用程序在WebSphere上运行,Oracle数据库配置为JDBC数据源。我们使用的是Oracle 10.1g。该应用程序是用Java 1.5编写的。
任何帮助都将不胜感激。
编辑:示例代码
DataSource ds; // spring configured
String sql = "INSERT INTO " + currentTable + " (" + stepId + ',' + stepEntryId + ", " + stepStepId + ", " + stepActionId + ", " + stepOwner + ", " + stepStartDate + ", " + stepDueDate + ", " + stepFinishDate + ", " + stepStatus + ", " + stepCaller + " ) VALUES (?, ?, ?, null, ?, ?, ?, null, ?, null)";
Connection conn = ds.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql);
// set values
stmt.executeUpdate();
// close connections
// later on in the code...
Connection conn = ds.getConnection();
PreparedStatement stmt = null;
ResultSet rset = null;
String sql = "SELECT " + stepId + ", " + stepStepId + ", " + stepActionId + ", " + stepOwner + ", " + stepStartDate + ", " + stepDueDate + ", " + stepFinishDate + ", " + stepStatus + ", " + stepCaller + " FROM " + currentTable + " WHERE " + stepEntryId + " = ?";
stmt = conn.prepareStatement(sql);
stmt.setLong(1, entryId);
rset = stmt.executeQuery();
//close connections
答案 0 :(得分:6)
默认情况下,您描述的行为应该是不可能的 - 在已提交的事务中进行的更改将立即对所有会话可用。但是,也有例外:
您是否正在使用COMMIT命令中的任何WRITE选项?如果不是,请确认COMMIT_WRITE初始化参数的值。如果要么使用“WRITE BATCH”或特别是“WRITE BATCH NOWAIT”,那么您可能会遇到并发问题。 “WRITE BATCH NOWAIT”通常用于写入事务的速度比可能的并发问题更重要的情况。如果初始化参数使用“WRITE”变体,则可以通过在提交中指定IMMEDIATE子句(see COMMIT)
在另一个事务提交之前,是否尝试读取调用SET TRANSACTION的数据的事务?使用SET TRANSACTION指定SERIALIZATION LEVEL READ ONLY或SERIALIZABLE将导致事务看不到调用SET TRANSACTION(see SET TRANSACTION)后发生的其他已提交会话发生的更改
编辑:我看到你正在使用DataSource类。我不熟悉这个类 - 我认为它是一个连接共享资源。我意识到您当前的应用程序设计可能无法在整个工作流程中轻松使用相同的连接对象(这些步骤可能设计为独立运行,并且您没有构建设施以将连接对象从一个步骤传递到但是你应该验证返回到DataSource对象的连接对象是否“干净”,特别是关于打开的事务。您可能没有在代码中调用SET TRANSACTION,但是其他地方的DataSource的另一个消费者可能正在这样做,并且将连接返回到数据源,并且会话仍处于SERIALIZABLE或READ ONLY模式。在进行连接共享时,必须先将所有连接回滚,然后再将其传递给新的用户。
如果您无法控制或查看DataSource类的行为,您可能希望尝试对新获取的连接执行ROLLBACK,以确保它没有已建立的延迟事务。
答案 1 :(得分:4)
如果DBA团队尝试修改max_commit_propagation_delay
参数,则可能意味着您正在连接到RAC实例(即:几个不同的服务器访问一个数据库)。
在这种情况下,当您在Java代码中关闭并重新打开连接时,有可能您将被另一台服务器应答。延迟参数意味着当两个实例不在完全相同的时间点时存在小的时间帧。你得到的答案与时间点一致,但可能不是最新的。
正如KM所提出的,最简单的解决方案是在提交后保持连接打开。
或者,您也可以在关闭连接后添加延迟(如果这是实际的(如果这是批处理作业,并且响应时间不是很重要)。
答案 2 :(得分:1)
是否使用ORM?它可能是从缓存中选择而不是在更改后形成数据库。
答案 3 :(得分:0)
这听起来像是RAC的问题,连接到两个不同的实例,并且SCN不同步。
作为一种解决方法,请考虑不关闭数据库连接并获取新连接,但重用相同的连接。
如果这不可行,则向尝试检索插入行的查询添加重试。如果未返回该行,则稍微休眠一下,然后再次重试该查询。将其放入循环中,经过指定次数的重试后,您就会失败。
[附录]
在他的回答中,Steve Broberg(+1!)提出了有趣的想法。我没想过:
COMMIT
可能不是IMMEDIATE WAIT
我确实考虑了闪回查询的可能性,并且在没有提及的情况下将其解除了,因为没有明显的理由OP会使用闪回查询,并且在代码片段中没有这样的证据。)
[/附录]
答案 4 :(得分:0)
可能的解决方法可能是使用JTA事务。 它通过多个打开/关闭jdbc conns使您的连接在“幕后”打开。也许它会将您的连接保持在同一台服务器上并避免此同步问题。
UserTransaction transaction = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");
transaction.begin();
// doing multiple open/close cxs
transaction.commit();
答案 5 :(得分:0)
代码段实际上并未包含提交。
如果您假设/依赖执行提交的close连接,它可能不是同步的(即,当Java告诉Oracle关闭连接时,java可能会将连接报告为关闭,这意味着它可能在提交之前由Oracle完成。
答案 6 :(得分:0)
我看到你的代码中没有提交。它们是这样一个应用程序中最重要的语句,所以我希望每次都明确地写出它们,而不是依赖close()等。
默认情况下,您的连接上也可能将autocommit设置为true,这将准确地解释行为(它在每次插入/更新后提交)。
您能否检查一下您是否准确提交了所需内容,例如:在交易结束时而不是之前?
如果在部分通过时有提交,那么你的线程之间会出现竞争条件,这也可以解释为什么在负载较大时会出现更多问题。
答案 7 :(得分:0)
“即使在成功完成之前运行的插入/更新提交。”
这告诉我你发出了一个commit(),之后又希望再次读取完全相同的数据(这是可重复的读取)。
这告诉我你不应该犯。只要你想确保NO OTHER TASK能够修改你明显要保持稳定的任何数据,你就无法释放锁(这就是提交的作用)。
请注意,当您对某些资源保持锁定时,其他线程将堆叠“等待该资源变为可用”。当您释放锁时,该堆栈非空的可能性随着一般系统负载的增加而变得更高。当你(最终)发出“提交”时,你的DBMS将得出的结论是,“嘿,哇,这家伙终于完成了这个资源,所以现在我可以让所有其他等待的人尝试做他们的事情(并没有防止“他们的东西”被更新!)“。
可能存在与我忽略的Oracle快照隔离有关的问题。如果是这样抱歉。