java - 为什么我的Oracle PreparedStatement有时即使没有争用也永远不会返回?

标签 java oracle jdbc prepared-statement freeze

2014年2月5日更新:

通过重新启动承载Oracle数据库的Linux服务器,解决了问题。自从去年5月以来,就没有启动过该服务器,尽管Oracle本身已经定期重新启动过。

我有几个Java 1.6程序,它们使用Oracle 11.2数据库和11.2.0.3.0 ojdbc6.jar Oracle驱动程序。在看似随机的点,它显然会挂起,永远不会从PreparedStatement.executeUpdate()返回控制。

通常,我的程序将数据绑定(bind)到BLOB列,并且在这种情况下(还是随机发生),它可能会卡在对OutputStream.flush()的调用上,其中,我的OutputStream是OracleBlobOutputStream的包装器。

在这两种情况下,线程都被卡住,一直等待它试图继续读取Oracle响应的套接字。

使用sqlDeveloper监视JDBC瘦客户机的Oracle数据库中的 session ,可以看到 session 正在等待,如Seconds In Wait所示。在刷新blob的特定情况下,ActiveSQL选项卡显示“无可用文本”。如果卡在PreparedStatement.executeUpdate()上,则该选项卡将显示我的插入语句的全文。无论哪种情况,“等待”选项卡都将显示“SQL *从客户端获取更多数据”,这对我来说表明Oracle服务器正在等待更多数据来完成客户端请求。

因此,我可以看到Oracle服务器似乎正在等待客户端完成他的请求。客户端似乎已经完成了请求,并且正在等待服务器返回响应。

网络错误可能是造成这种情况的原因吗?我认为客户端和服务器将受到TCP / IP流的重试逻辑的保护。我经常在Internet上(相对于数据库的测试实例)通过VPN连接使用此应用程序,在该应用程序上我会期望出现更多错误,但是在那种情况下我从来没有发现问题。

我已经在Oracle驱动程序中看到针对getNextPacket()问题的修复程序,但是如上所示,我们正在使用最新的驱动程序,并且应该具有这些驱动程序。

正如我所期望的,“争用”选项卡从不指示任何内容。从我可以说的所有事情来看,竞争并不是这里的问题。而且该程序将在晚上仍然失败,因为除我的程序外几乎没有其他 Activity 。

该代码在我的测试环境中可以完美地工作。它也可以在客户站点的测试环境中工作。但是在生产环境中它失败了。它可能会在失败之前插入50-100K数据行。

在某些情况下,它不会挂起。它引发不一致的异常,例如关于如何只能将LONG值绑定(bind)到LONG列的异常。在四个不同的数据库上进行测试时,我也从未见过这种情况,问题从一个表转移到另一个表且没有可识别的模式。

据我所知,动态SQL将起作用,并且该问题特定于准备好的语句。但是我不能确定。

该生产数据库大于任何测试实例。它的大小可以处理大约2 TB的数据,并且在达到该目标的途中可能约为1/3。所有表空间都有足够的空间,并且回滚段最近扩大了3倍,并且利用率很低。

我不知道自动提交模式中的挂起,并且似乎仅在事务累积大量数据后挂起。但是由于问题如此随机,我无法得出结论。

该程序运行了数月没有问题,然后在几周前就开始了,而对软件没有任何更改。客户的数据库一直在稳步扩大,所以这是一个变化。而且我听说客户端大约在那个时候安装了一些网络监控软件,但是我没有任何细节。

有时JDBC批处理正在进行中,而其他时候则没有,并且仍然失败。

我要把头发拉出来,这没什么可做的!

我的 friend 在stackoverflow上有何见解?

这是一个调用堆栈,我在这里等待在服务器上看到“等待秒数”,然后在Eclipse调试器中暂停了我的客户端程序。从OracleOutputStream起,所有内容都是ojdbc6.jar代码。

Thread [GraphicsTranslator:1] (Suspended)        
owns: T4CConnection  (id=26)    
owns: Input  (id=27)       
SocketInputStream.socketRead0(FileDescriptor, byte[], int, int, int) line: not available [native method]             
SocketInputStream.read(byte[], int, int) line: 129             
DataPacket(Packet).receive() line: 293   
DataPacket.receive() line: 92      
NetInputStream.getNextPacket() line: 174          
NetInputStream.read(byte[], int, int) line: 119   
NetInputStream.read(byte[]) line: 94     
NetInputStream.read() line: 79 
T4CSocketInputStreamWrapper.readNextPacket() line: 122        
T4CSocketInputStreamWrapper.read() line: 78  
T4CMAREngine.unmarshalUB1() line: 1040           
T4CMAREngine.unmarshalSB1() line: 1016            
T4C8TTIBlob(T4C8TTILob).receiveReply() line: 847            
T4C8TTIBlob(T4C8TTILob).write(byte[], long, byte[], long, long) line: 243               
T4CConnection.putBytes(BLOB, long, byte[], int, int) line: 2078  
BLOB.setBytes(long, byte[], int, int) line: 698      
OracleBlobOutputStream.flushBuffer() line: 215               
OracleBlobOutputStream.flush() line: 167            
ISOToDBWriter.bindElementBuffer(ParameterBinding, SpatialObject, boolean) line: 519               
ISOToDBWriter.writePrimitive(SpatialObject, boolean) line: 1720              
ISOToDBWriter.writeDgnElement(SpatialObject, Properties, String, boolean) line: 1427 
ISOToDBWriter.write(SpatialObject) line: 1405   
ISOHandler.inputObject(InputEvent) line: 864    
InputEventMulticaster.inputObject(InputEvent) line: 87               
Input(Input).notifyInput(Object, Object) line: 198            
Input(Input).notifyInput(Object) line: 157            
Input.readElement(int) line: 468               
Input.readElement() line: 403    
Input.run() line: 741        
GraphicsTranslator.processAllDgnFiles() line: 1190            
GraphicsTranslator.run() line: 1364          
Thread.run() line: 662    

2014年2月3日更新:

我已经能够在客户的站点上进行更多测试。显然问题是由网络错误引起的。我用直接jdbc调用编写了一个小型测试程序,但它也失败了。它仅针对此特定数据库实例失败。测试程序将越来越长的字符串绑定(bind)到一个准备执行的语句中,该语句将继续执行并最终回滚事务(如果达到那么远)。测试程序而不是挂起,有时会随机抛出一个异常,如下所示:
java.sql.SQLException: ORA-01461: can bind a LONG value only for insert into a LONG column
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:447)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:951)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:513)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:227)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:531)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:208)
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1046)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1336)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3613)
at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3694)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1354)
at com.byers.test.outage.TestPreparedInsert.insertThenRollback(TestPreparedInsert.java:81)
at com.byers.test.outage.TestPreparedInsert.runTest(TestPreparedInsert.java:54)
at com.byers.test.outage.TestPreparedInsert.main(TestPreparedInsert.java:28)

测试程序将插入数千行,并在相当不错的剪辑下运行,直到插入字符串的长度超过大约1,300字节为止。然后,它变得越来越慢,到字符串约1,500个字节时,单个插入将花费30秒或更长时间。我怀疑问题是在请求超出数据包大小时开始的。

我运行了WireShark,并捕获了我和Oracle服务器之间传输的所有IP数据包。然后,我看到许多未确认的TCP ACK段,未捕获的TCP前段,TCP Dup ACK 3#1,TCP Dup ACK 3#2等。我不是网络专家,但我很聪明地说:“这不是好”。

与生产系统不同,到目前为止,我的测试程序实际上并未导致Oracle“挂起”。 Oracle session 不会显示“等待中的秒数”,并且如果我等待足够长的时间,程序将继续运行(即使我对此的耐心受到了限制)。除非同时运行多个程序实例,否则我也看不到抛出以上异常,尽管那也可能是因为等待时间不够长的问题?

调用以下代码,例如:
insertThenRollback(con, 50, 2000, 0);

非常善于产生错误。有趣的是,从大插入字符串(如3000字节)开始不会导致错误,直到程序以4000循环并返回到1300+范围为止。
private static void insertThenRollback(Connection con, int delayMs, int rowCount, int startCharCount)
        throws SQLException, InterruptedException
{
    System.out.println("Batch " + (++batchCount) + ". Insert " + rowCount + " rows with "
            + delayMs + "ms. delay between, then rollback");
    String sql = "Insert Into config (name,value) values(?,?)";
    PreparedStatement stmt = con.prepareStatement(sql);
    String insString = "";
    for (int c = 0; c < startCharCount; ++c)
    {
        int randomChar = (int) (Math.random() * DATA_PALLET.length());
        insString += DATA_PALLET.charAt(randomChar);
    }
    try
    {
        for (int i = 0; i < rowCount; ++i)
        {
            if (insString.length() > MAX_INSERT_LEN - 1)
                insString = "";
            int randomChar = (int) (Math.random() * DATA_PALLET.length());
            insString += DATA_PALLET.charAt(randomChar);
            String randomName = "randomName--" + UUID.randomUUID();
            System.out.println("Row " + (i + 1) + "->" + randomName + '/' + insString.length()
                    + " chars");
            stmt.setString(1, randomName);
            stmt.setString(2, insString);
            stmt.executeUpdate();
            Thread.sleep(delayMs);
        }
    }
    finally
    {
        System.out.println("Rollback");
        con.rollback();
        stmt.close();
    }
}

这似乎使我站稳脚跟,告诉客户问题出在他们的网络上。你们都同意吗?客户端是否应该能够以某种方式监视其网络中的此类错误,这不是真的吗?对于我来说,我们花数百个小时的集体努力来追逐这样的问题,只是为了发现这是硬件还是某种侵入性软件,这对我来说似乎很愚蠢。有没有办法通过监视某种方式来检测这些网络错误的严重程度?

最佳答案

最近,我们在生产应用程序中具有相同的行为,重新启动应用程序后,数据库持久性恢复工作。

为了寻找可能发生的情况的线索,我最终在本文(Understanding JDBC Internals & Timeout Configuration)中详细解释了JDBC如何工作以及其不同类型的Timeout。

我们将尝试在JDBC上配置超时(使用 oracle.net.CONNECT_TIMEOUT /
oracle.jdbc.ReadTimeout ),以尝试将来避免此问题。

关于java - 为什么我的Oracle PreparedStatement有时即使没有争用也永远不会返回?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21510353/

相关文章:

java - Oracle JDBC连接超时问题

java - 更新中缺少表达式

Java MySQL 到 SQlite

java - 当前不允许连接到相机 "1"

oracle - SQL Developer 不显示 XML

java - 检测数据库已关闭

java - JDBC 无法从单独的类连接到我的数据库。 DB 类创建连接

java - 覆盖 getView() 的问题

java - 神经网络 : classify as nothing?

java - 我不知道这个 void 方法是如何工作的