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

时间:2017-05-12 11:58:16

标签: java oracle hang ojdbc

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

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

"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盒解决的类似问题,但这对我们来说不是一个选择;我需要在应用程序级别修复。

使用以下命令定义DB资源:

<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());

2 个答案:

答案 0 :(得分:1)

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

Question on network timeouts

答案 1 :(得分:1)

检查会话是否:

  1. 无效 - 如果处于非活动状态,等待状态是什么,可能正在等待客户端或网络。
  2. 有任何涉及空闲会话的阻塞锁(例如,它对未提交的事务持有一些锁)。
  3. 同时检查连接风暴(即访问实例的会话太多会导致严重的CPU问题)