Java JDBC Oracle SQL 查询每隔几个月挂起一次

标签 java oracle freeze ojdbc

我们有一个带有 Web UI 和 REST API 的多线程 java 应用程序,该应用程序使用 java 6 编译并在 tomcat 6 中运行。在操作期间,它每天使用 OJDBC 访问其 Oracle 数据库数百万次。每两三个月就会有一个数据库查询挂起并且永远不会返回,这会导致部分应用程序停止处理并产生积压。其他线程能够与数据库通信并完成它们的工作,只有一个线程挂起,不幸的是停止了文件处理。

线程转储显示线程正在从一个永远不会超时也不会关闭的套接字中读取:

"FileUpload" daemon prio=10 tid=0x00002b8e60617800 nid=0xf9e runnable [0x00002b8e5e10b000]
java.lang.Thread.State: RUNNABLE
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at oracle.net.ns.Packet.receive(Packet.java:311)
    at oracle.net.ns.DataPacket.receive(DataPacket.java:103)
    at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:312)
    at oracle.net.ns.NetInputStream.read(NetInputStream.java:257)
    at oracle.net.ns.NetInputStream.read(NetInputStream.java:182)
    at oracle.net.ns.NetInputStream.read(NetInputStream.java:99)
    at oracle.jdbc.driver.T4CSocketInputStreamWrapper.readNextPacket(T4CSocketInputStreamWrapper.java:121)
    at oracle.jdbc.driver.T4CSocketInputStreamWrapper.read(T4CSocketInputStreamWrapper.java:77)
    at oracle.jdbc.driver.T4CMAREngine.unmarshalUB1(T4CMAREngine.java:1173)
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:309)
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:200)
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:543)
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:238)
    at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:1244)
    at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1492)
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1710)
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:4372)
    at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:4453)
- locked <0x00002b8e1c2d7010> (a oracle.jdbc.driver.T4CConnection)
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:6270)
    at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
    at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:96)
    at xxx.OracleFileInfoDAO.getFilesToUpload(OracleFileInfoDAO.java:874)

发生这种情况时,DBA 已经查看了数据库服务器,但没有看到长时间运行的查询。解决方案是回收 tomcat 来解决这个问题,但我想看看是否有一种编程方式来处理这个问题。我看到附录提到类似的问题,这些问题通过回收运行数据库服务器的 LINUX 机器来解决,但这对我们来说不是一个选择;我需要在应用程序级别进行修复。

数据库资源使用以下方式定义:

<Resource auth="Container" description="Oracle Datasource" name="xxx" scope="shareable" type="javax.sql.DataSource" url="jdbc:oracle:thin:@xxx:1521/xxx"  driverClassName="oracle.jdbc.driver.OracleDriver" username="xxx" password="xxx" maxWait="5000" maxActive="100" maxIdle="20" removeAbandoned="true" testOnReturn="true" testOnBorrow="true" validationQuery="select 1 from dual" />   

使用的OJDBC驱动是:ojdbc6_g-11.2.0.4.0.jar

数据库版本为:11.2.0.3.0

执行查询的java代码是:

                con = CSAConnectionManager.getConnection();                     
            StringBuilder strBuf = new StringBuilder(SQL_SELECT_FILE_INFO_TO_UPLOAD);
            ps = con.prepareStatement(strBuf.toString());
            ps.setString( 1, hostname );
            ps.setString( 2, containerId );
            ps.setMaxRows( maxRows );

            Date before = new Date();
            ResultSet rs = ps.executeQuery();

这是 getConnection() 的来源:

    public static Connection getConnection() throws Exception
{
    return instance.getInstanceConnection();
}

public Connection getInstanceConnection() throws Exception
{
    Connection con = null;
    if(ds != null)
    {
        con = ds.getConnection();
    }
    else
    {
        String dburl = wrapper.getDBUrl();
        String username = wrapper.getDBUserName();
        String password = wrapper.getDBPassword();      
        String driverClass = wrapper.getDBDriverClass();
        Class.forName(driverClass).newInstance();

        con = DriverManager.getConnection(dburl,username,password);
    }       
    con.setAutoCommit(false);
    return con;
}

“ds”定义为:private static DataSource ds = null; 并使用以下方式初始化:

        Context initContext = new InitialContext();
        ds = (DataSource)initContext.lookup(wrapper.getCSADBJNDIName());

最佳答案

根据我的经验,这通常是网络错误。您的查询已完成,但您的客户端仍在阻塞它永远不会收到的网络响应。这就是为什么弹跳应用程序服务器可以正常工作,因为它会重置应用程序服务器中的所有内容,但是弹跳数据库服务器没有任何意义,因为这不是数据库问题。看看这个网站上的这个问题/答案......

Question on network timeouts

关于Java JDBC Oracle SQL 查询每隔几个月挂起一次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43937230/

相关文章:

regex - 从大约 5 个单词中以任意顺序匹配至少 3 个单词

sql - 使用从第二行到最后一行的数据更新最后一行

sql - 对 Oracle 时间戳使用 BETWEEN 或 <=, >=

c# - 完成构建后,Visual Studio 2015 卡住

java - 如何获取特定周内特定日期的日期

java - Rabbitmq 连接限制?

android - 应用程序在调试时卡住

c# - 有谁知道为什么我会收到 HttpWebRequest 超时?

java - 选择在应用程序初始化时启动哪个 Activity ?

java - 通过实例引用访问静态成员(使用 'this' 关键字)