Statement.setFetchSize(nSize) 方法在 SQL Server JDBC 驱动程序中真正做了什么?

2022-08-31 14:55:17

我有一个非常大的表格,每天有数百万条记录,每天结束时,我都会提取前一天的所有记录。我这样做是这样的:

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

问题是该程序需要2GB的内存,因为它将所有结果都保存在内存中,然后进行处理。

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

有没有办法像Oracle数据库驱动程序那样以小块的形式读取结果,当执行查询时,只显示几行,并且向下滚动时会显示更多结果?


答案 1

在 JDBC 中,该方法对于 JVM 中的性能和内存管理非常重要,因为它控制从 JVM 到数据库的网络调用次数,并相应地控制用于 ResultSet 处理的 RAM 量。setFetchSize(int)

本质上,如果调用 setFetchSize(10) 而驱动程序忽略它,则可能只有两个选项:

  1. 尝试使用其他 JDBC 驱动程序,该驱动程序将遵循获取大小提示。
  2. 查看连接上特定于驱动程序的属性(创建连接实例时的 URL 和/或属性映射)。

RESULT-SET 是为响应查询而在 DB 上封送的行数。ROW-SET 是每次从 JVM 到 DB 的调用从 RESULT-SET 中读取的行块。这些调用的数量和处理所需的 RAM 取决于提取大小设置。

因此,如果 RESULT-SET 有 100 行,而 fetch-size 为 10,则在任何给定时间将使用大约 10*{row-content-size} RAM 来检索所有数据。

默认的读取大小为 10,这相当小。在发布的情况下,驱动程序似乎忽略了提取大小设置,在一次调用中检索所有数据(大RAM要求,最佳最小网络调用)。

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

所有这些都取决于驱动程序,因为设置只是一个“提示”,但在实践中,我发现这就是它适用于许多驱动程序和数据库的方式(在许多版本的Oracle,DB2和MySQL中进行了验证)。


答案 2

该参数是对 JDBC 驱动程序的提示,提示从数据库中一次性读取许多行。但司机可以自由地忽略这一点,做它认为合适的事情。某些驱动程序(如 Oracle 驱动程序)以块的形式读取行,因此您可以读取非常大的结果集,而无需大量内存。其他驱动程序只是一次性读取整个结果集,我猜这就是您的驱动程序正在做的事情。fetchSize

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


推荐