Java应用程序如何解决Oracle TNS效率低下(往返次数多,延迟高)的问题?

7
我在查看一个非常慢的SQL查询(来自使用Hibernate部署在JBoss 5.1中的Java应用程序)。这个特定的查询返回大约10K条记录,但仍然需要40秒或更长时间。
我最终使用数据库流量嗅探工具(wireshark有一个TNS解析器)找到了一些意外的情况。当数据从服务器传输时,每个结果行都在自己的TNS数据包中。此外,每个TNS数据包在发送到数据库之前都会被客户端(即应用服务器)确认。对于10K条记录,需要进行10K次往返以获取数据包并确认它。对性能的影响是巨大的。
这是非常低效的。TCP允许更大的数据包,并且具有许多机制(滑动窗口、延迟ACK)来减少延迟并增加吞吐量。然而,在这种情况下,是TNS协议在顶层添加了自己的协商。
如果我从Oracle的SQL Developer运行相同的查询,我不会看到这种模式。查询在大约1/10的时间内完成,没有成千上万次的往返。
简短版本:Oracle的传输协议(TNS)似乎将数据传递到一个TNS数据包中的每个查询结果行,并要求在服务器发送下一个数据包之前客户端确认每个数据包。
我在这里找到了一些关于此的信息(请向下滚动直到'tnsnames.ora文件中的SDU和TDU参数'部分)。
因此,我的问题是:是否可以控制Oracle驱动程序(我正在使用10.2.0.4.0),以使TNS协议更有效?再次强调,这是一个在JBoss中部署的相当标准的J2EE应用程序。
非常感谢!

嘿,感谢您深入的问题,并向有类似情况的人提供了一些提示。投票支持。 - TonyP
2个回答

6
调整 tnsnames.ora 和 listener.ora 中的 SDUTDU 参数。
将当前语句的批处理大小设置为 100。
  ((OracleStatement)stmt).setRowPrefetch (100);

注意:
设置预取大小会影响应用程序的性能。增加预取大小将减少获取所有数据所需的往返次数,但会增加内存使用量。这将取决于查询中列的数量和大小以及预计返回的行数。它还将取决于JDBC客户端机器的内存和CPU负载。独立客户端应用程序的最优值与高负载应用程序服务器不同。还应考虑网络连接的速度和延迟。
(来自Oracle Database JDBC Developer's Guide and Reference
可用的连接属性在此处查看。
还要看看Oracle UCP

谢谢,通过搜索行预取,我在http://download.oracle.com/docs/cd/B19306_01/java.102/b14355/oraperf.htm上找到了以下信息:“标准JDBC一次接收一行结果集,每行需要往返到数据库”,这解释了我所看到的问题。嗯…是否可能在数据源级别上进行设置? http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration.html 的参数列表中没有预取设置。 - wishihadabettername
1
与此同时,我在http://download.oracle.com/docs/cd/B19306_01/java.102/b14355/urls.htm#i1006362找到了一个驱动程序属性(defaultRowPrefetch)。 - wishihadabettername
你正在使用UCP吗?http://www.oracle.com/technetwork/database/features/jdbc/index-091264.html - oluies

3

尝试增加语句对象的获取大小。

我认为默认值是10,所以您可以尝试从100开始。

Statement stmt = connection.createStatement();
stmt.setFetchSize(100);
ResultSet rs = stmt.executeQuery("SELECT ...");

通过增加获取大小确实填充了TCP数据包,从而改善了事情。 - wishihadabettername

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