SQL Server JDBC驱动程序中的Statement.setFetchSize(nSize)方法实际上做了什么?

我有这样一张真正的大桌子,每天有数百万条logging,每天结束的时候我都会提取前一天的所有logging。 我这样做是这样的:

String SQL = "select col1, col2, coln from mytable where timecol = yesterday"; Statement.executeQuery(SQL); 

问题是这个程序需要2GB的内存,因为它将所有的结果存储在内存中,然后处理它。

我尝试设置Statement.setFetchSize(10)但它从操作系统采用完全相同的内存,它没有任何区别。 我正在使用Microsoft SQL Server 2005 JDBC驱动程序

有没有什么办法像Oracle数据库驱动程序那样在小块中读取结果,当查询被执行时只显示几行,当你向下滚动显示更多的结果?

在JDBC中, setFetchSize(int)方法对JVM中的性能和内存pipe理非常重要,因为它控制从JVM到数据库的networking调用数量,以及相应的用于ResultSet处理的RAM数量。

本质上,如果调用setFetchSize(10)并且驱动忽略它,那么可能只有两个选项:

  1. 尝试使用不同的JDBC驱动程序来遵守提取大小提示。
  2. 查看Connection上的驱动程序特定属性(创buildConnection实例时的URL和/或属性映射)。

RESULT-SET是响应查询的数据库中编组的行数。 ROW-SET是从JVM到DB的每个调用中从RESULT-SET中取出的行的块。 这些调用和处理所需的RAM的数量取决于获取大小设置。

因此,如果RESULT-SET具有100行且读取大小为10,那么将有10个networking调用来检索所有数据,在任何给定时间使用大致10 * {row-content-size} RAM。

默认的读取大小是10,这是相当小的。 在发布的情况下,似乎驱动程序将忽略获取大小设置,在一次调用中检索所有数据(大内存要求,最佳最小networking调用)。

ResultSet.next()下面发生的事情是,它实际上并不从RESULT-SET中一次获取一行。 它从(本地)ROW-SET中获取,并从服务器获取下一个ROW-SET(不可见),因为它在本地客户端上耗尽。

所有这一切都取决于驱动程序,因为该设置只是一个“提示”,但实际上我发现它是如何工作的许多驱动程序和数据库(在许多版本的Oracle,DB2和MySQL中validation)。

fetchSize参数是JDBC驱动程序提示许多行从数据库中一次获取的提示 。 但司机可以自由地忽略这一点,并做出它认为合适的事情。 一些驱动程序(如Oracle)以块的forms提取行,因此您可以读取非常大的结果集而不需要太多的内存。 其他车手一次只读完整个结果集,我猜这就是你的车手正在做的事情。

您可以尝试将驱动程序升级到SQL Server 2008版本(可能更好),或者开源的jTDS驱动程序。

您需要确保Connection上的自动提交已closures ,否则setFetchSize将不起作用。

 dbConnection.setAutoCommit(false); 

编辑:记得当我使用这个修复它是Postgres特定的,但希望它仍然可以用于SQL Server。

听起来像mssql jdbc正在为你缓冲整个结果集。 你可以添加一个连接string参数,说selectMode = cursor或responseBuffering = adaptive。 如果您使用的是2005版mssql jdbc驱动程序的2.0以上版本,则响应缓冲应该默认为自适应。

http://msdn.microsoft.com/en-us/library/bb879937.aspx

声明界面Doc

摘要: void setFetchSize(int rows)当需要更多行时,为JDBC驱动程序提供应该从数据库中提取的行数。

阅读这本电子书J2EE和超越艺术泰勒

在我看来,你真的想限制查询和页面中返回的行通过结果。 如果是这样,你可以做一些事情:

 select * from (select rownum myrow, a.* from TEST1 a ) where myrow between 5 and 10 ; 

你只需要确定你的界限。

尝试这个:

 String SQL = "select col1, col2, coln from mytable where timecol = yesterday"; connection.setAutoCommit(false); PreparedStatement stmt = connection.prepareStatement(SQL, SQLServerResultSet.TYPE_SS_SERVER_CURSOR_FORWARD_ONLY, SQLServerResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(2000); stmt.set.... stmt.execute(); ResultSet rset = stmt.getResultSet(); while (rset.next()) { // ......