在Tomcat Servlet中,SQL执行时间比普通Java程序慢得多

5
出乎意料的是,今天早上我曾遇到慢查询的两个问题,性能突然提高了。我不知道为什么。
我对服务器没有权限,可能有人更改了一些设置。
问题已经不存在了
简而言之:
  • s.executeQuery(sql)在Tomcat Servlet中运行非常缓慢。
  • 相同的查询在同一台机器上的简单Java程序中运行良好。
  • 并非所有查询在Servlet中都很慢。只有一些较大的查询会缓慢。
  • 在另一台机器上运行相同的Servlet速度很快。

更新

请阅读下文的更新!
我有一个servlet通过JSON执行SQL请求并返回结果。由于某些原因,一些请求需要花费大量时间来执行,但是当我在任何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,查询迅速返回。因此,问题来自于我的生产服务器或Tomcat配置。有什么配置项可能会影响这个吗?

更新4: 我在一个普通的Java程序中尝试了相同的代码(仍然在同一台服务器上),结果非常快。因此,问题来自于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)这个位置?


客户端是否在与“jdbc”客户端相同的机器上?批处理呢? - Jayan
1
你是在同样的基础上进行比较吗?你的代码打印结果集,SQL客户端也是一样吗?通常查询运行非常快,但传输数据并显示数据也需要很长时间。顺便说一句:你应该使用连接池。你是否检查过登录时花费的时间?还有一个提示:你可以使用dbVisualizer连接数据库。它是用相同语言(Java)编写的,但经过优化。所以你可以看看问题是在Java/驱动程序中还是在你的代码中。 - Marged
使用prepareStatement并将resultSet的fetchSize设置为1000左右,这将减少I/O操作的次数。 - Kalyan Chavali
任何Oracle SQL客户端”到底是哪个客户端?这些客户端使用的技术是什么? - user330315
澄清一下:我使用Aqua Data Studio,但Toad也是一样的。其他(更大的)SQL查询运行得非常好。此外,它与结果数量无关。即使只有一个结果,我的查询也很慢。我会看看dbVisualizer,谢谢。 - Tim
显示剩余8条评论
5个回答

1
在某些情况下(不确定是否适用于您的情况),在Statement对象上设置fetchSize可以显著提高性能。这取决于正在获取的resultSet的大小。

尝试将其设置为Oracle默认值10之外的较大值(请参见this link)。

请参见Statement.setFetchSize

谢谢,但它没有改变任何东西。 - Tim

1
根据您的症状,我认为您的问题不在于SQL客户端代码,而是与服务器有关。堆栈显示您的客户端正在等待响应。这与您可以在单独的进程中运行客户端而没有任何问题相符。因此,您可能需要查看SQL服务器运行缓慢的系统原因以及它如何与Tomcat相关联。我的经验表明,像这样的情况通常是由于磁盘问题引起的,因此我倾向于检查在Tomcat加载时是否由于RAM不足而进行分页,或者由于减少了磁盘缓存而导致磁盘操作更高。假设您正在运行UNIX变体,则建议检查工作和故障情况下的vmstat和iostat以消除此类问题。

我已经检查了这些参数,甚至为Tomcat更改了jvm选项。然而,由于不可解释的原因,今天早上性能提高了,我的问题已经解决了。我不知道为什么。我对服务器没有权限,也许有人更改了什么。 - Tim
哦,好吧。至少现在它可以工作了。顺便说一下,我原以为你的数据库和Web服务器在同一台机器上。如果不是的话,你可能会发现,在处理大查询时,数据库由于通常与资源限制相关的问题而表现不佳。 - Peter Brittain

0

出乎意料的是,今天早上性能提高了,我的问题不再存在。我不知道为什么。我对服务器没有权限,可能有人改了什么。


0

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

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

检查Java和DB服务器之间的网络延迟。与上述情况类似。可能需要使用traceroute进行诊断?


数据库性能不是问题 - 在同一台机器上的servlet之外运行相同的查询速度很快。 - Tim
应用程序和数据库服务器之间的网络延迟如何? - bubooal
我也将其排除在外,因为大多数SQL在servlet中运行速度很快。现在新的也是如此。 - Tim

0

由于您没有放置查询,我可以给您一个可能的情况。如果您在查询中使用像to_char等的函数,则在通过JDBC执行查询时不会使用表索引,但在控制台中运行时将正常工作。我不确定为什么,但是与JDBC驱动程序有关。我在db2中遇到了完全相同的问题,并通过删除函数的使用来解决它。

另一种情况可能是正在获取大量记录并且未实现适当的批处理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接