SQL 在 Tomcat Servlet 中的执行时间比在正常的 Java 程序中慢得多

SQL execution time much slower in a Tomcat Servlet than in a normal Java program

然而,由于莫名其妙的原因,今天早上我的两个查询的性能提高了,这两个查询过去很慢。我不知道为什么。
我对服务器没有权限,也许有人改变了什么。
问题没有了

简而言之:

更新

请阅读正文下方的更新!

我有一个 servlet 执行 SQL 请求并通过 JSON 发回结果。出于某种原因,某些请求需要花费大量时间来执行,但是当我 运行 在任何 Oracle SQL 客户端中执行它们时,它们会立即执行。

我说的是相同的 SQL 相差 1 秒和 5 分钟(这并不复杂)。

这怎么解释? 有没有办法提高基于 java 的 SQL 请求的性能?

我正在使用执行查询的传统方式:

java.sql.Connection conn = null;
java.sql.Statement s = null;
ResultSet rs = null;

String dbDriver = "oracle.jdbc.driver.OracleDriver";
String dbConnectionString = "jdbc:oracle:thin:@" + dbHost + ":" + dbPort + ":" + dbSid;

Class.forName(dbDriver).newInstance();
conn = DriverManager.getConnection(dbConnectionString, dbUser, dbPass);
s = conn.createStatement();
s.setQueryTimeout(9999);
rs = s.executeQuery(newStatement);
ResultSetMetaData rsmd = rs.getMetaData();

// Get the results
while (rs.next()) {
// collect the results
}

// close connections

我试过 ojdbc14 和 ojdbc6,但没有区别。

更新 1: 我在客户端计算机上的本地 Java 项目(不是 servlet)中尝试了相同的 SQL,我立即得到了结果。所以我认为问题出在我的 servlet 或 tomcat 配置 ?

更新 2: 罪魁祸首确实是 rs = s.executeQuery(mySql);我尝试使用 preparedStatement 代替,但没有区别。

更新 3: 我在本地 Tomcat 上创建了一个新的 Servlet 运行ning 并且查询很快返回。因此,问题来自我的生产服务器或 Tomcat 配置。知道什么配置项会影响这个吗?

更新 4: 我在普通 java 程序中尝试了相同的代码,而不是在 servlet(仍在同一台服务器上)中,结果很快。因此,问题来自 Servlet 本身(或 Tomcat?)。仍然不知道该怎么做,但我缩小了范围:)

更新 5: Jstack 显示以下内容(它从我的 servlet 所在的位置开始,其余部分我删掉了)

    "http-8080-3" daemon prio=3 tid=0x00eabc00 nid=0x2e runnable [0xaa9ee000]
   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:105)
        at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:305)
        at oracle.net.ns.NetInputStream.read(NetInputStream.java:249)
        at oracle.net.ns.NetInputStream.read(NetInputStream.java:171)
        at oracle.net.ns.NetInputStream.read(NetInputStream.java:89)
        at oracle.jdbc.driver.T4CSocketInputStreamWrapper.readNextPacket(T4CSocketInputStreamWrapper.java:123)
        at oracle.jdbc.driver.T4CSocketInputStreamWrapper.read(T4CSocketInputStreamWrapper.java:79)
        at oracle.jdbc.driver.T4CMAREngineStream.unmarshalUB1(T4CMAREngineStream.java:429)
        at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:397)
        at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:257)
        at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:587)
        at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:210)
        at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:30)
        at oracle.jdbc.driver.T4CStatement.executeForDescribe(T4CStatement.java:762)
        at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:925)
        at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1104)
        at oracle.jdbc.driver.OracleStatement.executeQuery(OracleStatement.java:1309)
        - locked <0xe7198808> (a oracle.jdbc.driver.T4CConnection)
        at oracle.jdbc.driver.OracleStatementWrapper.executeQuery(OracleStatementWrapper.java:422)
        

所以我卡在 java.net.SocketInputStream.socketRead0(Native Method) 了?

在某些情况下(不确定这是否适用于您的情况)在 Statement 对象上设置 fetchSize 会产生很大的性能改进。这取决于正在获取的结果集的大小。

尝试将其设置为大于 Oracle 的默认值 10(请参阅 this link)。

Statement.setFetchSize

鉴于您的症状,我相信您的问题不在于您的 SQL 客户端代码,而您实际上是在查看服务器的问题。堆栈显示您的客户端正在等待响应。这与您可以 运行 客户端在单独的进程中没有任何问题的事实相吻合。

因此,您可能需要查看 SQL 服务器 运行 运行缓慢的系统原因,以及这可能与 Tomcat 有何关联。我在这种情况下的经验通常是磁盘,所以我倾向于检查您是否在加载 Tomcat 时由于内存不足而进行分页,或者由于减少而遭受更高的磁盘操作磁盘缓存。假设您 运行 正在使用 UNIX 变体,我会查看 vmstat 和 iostat 以寻找工作和损坏的案例来消除此类问题。

然而,由于莫名其妙的原因,今天早上性能提高了,我的问题不再存在。我不知道为什么。我对服务器没有权限,也许有人改变了什么。

由于您的线程正在等待套接字读取,这意味着正在等待来自数据库服务器的响应,我会:

检查数据库性能,确保实例或查询在白天的某个时间点没有受到影响?

检查 Java 和数据库服务器之间的网络延迟。同上。可能是跟踪路由?

既然你没有输入query,我可以给你一个可能的场景。如果您在查询中使用 to_char 等函数,那么在通过 JDBC 执行查询时不会使用您的 table 索引,但可以正常工作 运行安慰。我不完全知道为什么,但是 JDBC 驱动程序有问题。我在 db2 中遇到了完全相同的问题,我解决了它,删除了函数的使用。

其他情况可能是正在获取大量记录并且未实施适当的批处理。