我有一个如下代码,
try (Connection connection = this.getDataSource().getConnection();
PreparedStatement statement = connection.prepareStatement(sqlQuery);) {
try {
statement.setFetchSize(10000); // Set fetch size
resultSet = statement.executeQuery();
while (true) {
resultSet.setFetchSize(10000);
boolean more = resultSet.next();
if (! more) {
break;
}
// populating an arraylist from the value from resultSet
}
}
catch (Exception e) {
LOGGER.error("Exception : "+e);
}
} catch (SQLException e) {
LOGGER.error("Exception : "+e);
}
我的理解如下,
语句提取大小为10000.当执行statement.executeQuery()时,它返回ResultSet游标。它将在内存中有10000行。 调用resultSet.next时,它从内存缓冲区中获取一行。 (每次通话一排)。当内存中没有更多行时,将再次触发查询,并再次从数据库中提取10000行并将其存储在缓冲区中。这将继续,直到没有要从DB
获取的行因此,如果我的理解是正确的, 总行数为210000,将会有多少实际数据库调用 ?是21岁吗? (210000/10000)
当调用DB时(当缓冲区中的行都被读取时)获取更多行(在我的情况下为10000)并存储在缓冲区中。缓冲区什么时候清除?
如果我理解错误,请纠正我。
我需要使用Oracle数据库中的数百万个数据。
感谢任何指针/信息
此致
SD
答案 0 :(得分:4)
抱歉,但您的理解是错误的。没有"再次触发查询"。
执行一次查询。这将花费初始时间来处理查询(除了优化查询之外,您无法执行任何操作),然后它将开始在服务器上生成行,这些行需要传输到客户端。在传输行时,服务器可能会继续生成更多要传输的行,并在服务器上缓冲它们。这种服务器端缓冲与我们在本Q& A中讨论的缓冲类型完全无关,而您几乎无法控制它。 (也许通过服务器配置,如果有的话。)在某些时候,所有行都将在服务器上收集,然后唯一剩下的事情就是将剩余的行从服务器传输到客户端。
因此,就客户端可以判断,一旦它将查询发送到服务器,服务器在考虑它时会有一定的延迟,之后行的速度通常一样快因为电线可以携带它们。因此,客户端开始使用resultSet.next()
读取这些行。
没有任何缓冲,每次调用resultSet.next()
都会从客户端向服务器发送请求,告诉它发送下一行,服务器只响应该行。这会非常快地产生第一行,但从长远来看效率非常低,因为它会导致客户端和服务器之间的往返过多。
通过缓冲,第一次调用resultSet.next()
将从服务器请求一堆行。这将对接收第一行的时间施加惩罚,因为您将不得不等待通过线路发送100行,但从长远来看,它将显着减少总网络开销,因为只会有每个行数的客户端和服务器之间的一次往返。
resultSet.setFetchSize()
的理想策略是保持原样,不要过于担心。
但是如果你对性能很偏执,那么一个好的策略是从一个相当小的提取大小开始(比如10),以便快速获得你的第一行,然后保持加倍直到达到某个最大(比如说100),超过这个数字,实际上没有任何进步。
答案 1 :(得分:3)
唯一可以回复您问题的人是Oracle JDBC驱动程序的作者。
据说,调用db来读取下一个数据块的时间不会超过几毫秒(或更少),大部分时间将取决于传输速率,以及可能的方式来自结果集。
我认为,一旦你每次通话超过几百个记录,你就会减少回报,设置更大的提取量。
关于清除对结果集的引用松开缓冲区(主要是垃圾收集域)。
请确保您的声明仅为FORWARD,仅出于性能原因和内存占用。
connection.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY );