2014-09-25 49 views
5

我正在使用MySQL并希望使用setFetchSize属性。默认的MySQL JDBC实现并不真正尊重它。如果将fetchsize设置为Integer.MIN_VALUE,它将单独获取每行,但考虑到我希望使用fetchSize的原因是我有足够的数据将我的内存使用量放入2G范围内,因此每行需要执行一个查询将需要永久。有没有会尊重fetchSize的mysql JDBC?

我想改为插入一个JDBC实现,该实现将与MySQL一起工作,并适当考虑获取大小,从而允许设置10,000或其他更高限制的fetchsize。任何人都可以指向我可以提供这种实现的jar吗?有没有任何其他资源,让我合理做一个查询包含成千上万的条目,是一种有效的方式,但在内存和所需的SQL查询数量。

+1

Integer.MIN_VALUE是负数,它如何获取一些负数行? – 2014-09-25 19:19:08

+0

@ElliottFrisch这就是MySQL JDBC驱动程序的工作方式。它要么提取(流)所有的东西,要么在指定'Integer.MIN_VALUE'时允许1x1检索。请参见http://dev.mysql.com/doc/connector-j/en/connector-j-reference-implementation-notes.html(在ResultSet下) – 2014-09-26 06:36:41

+2

_“必须每行执行一个查询”_我是否正确理解你认为fetchSize管理检索到的最大行数?因为这不会发生:单个查询仍会生成所有行,它只会逐行读取行。 – 2014-09-26 06:40:04

回答

4

要求图书馆的技术性问题是无关紧要的。也就是说,据我所知,MySQL没有其他驱动程序。您可以选择获取可能导致内存不足情况的所有行,或者通过设置setFetchSize(Integer.MIN_VALUE)来让驱动程序按需获取它们。

原因在于 - 我从Connector/J implementation notes得知 - 是因为MySQL协议不能在每个连接上打开多个游标,因此默认情况下会在执行时将所有行传送到客户端。

另一种选择是,检索行的一个接一个,但它带着你不能在处理ResultSet在同一连接上执行其它语句的问题:

There are some caveats with this approach. You must read all of the rows in the result set (or close it) before you can issue any other queries on the connection, or an exception will be thrown.

因此,只有MySQL的可以选择获取所有内容或一次获取一个。这意味着驱动程序无法尊重不同的读取大小。并且由于在逐个获取时需要注意,他们选择使用Integer.MIN_VALUE(而不是简单的1)作为在执行此操作之前应该真正考虑的信号。

一个可能的“中间”解决方案将要求您自己使用LIMITOFFSET编程并重复执行查询。

+0

谢谢你,我认为这是一个很好的机会,这是对数据库本身的一个限制,但是我很感谢你的确认,但是,我感到惊讶的是,有人还没有制作封装设置LIMIT和OFFSET,并在后台进行查询,以模拟setFetchSize的预期行为。我知道这不是一回事,但可以在javadoc中记录这种包装器的差异和优点/折衷 – dsollen 2014-09-26 12:28:46

+1

使用LIMIT/OFFSET你将多次执行你的查询,可能给你错误的结果(重复行,跳过其他行),除非你锁定TABLES,直到ResultSet关闭,然后解锁它们。代价昂贵,实现并不容易(人们需要解析查询以了解涉及哪些表),所以这不是一个好主意。或者,您可以创建TEMPORARY TABLE AS SELECT ...,并使用LIMIT/OFFSET读取,但是这很容易出现幻像读取,并且又昂贵。 setFetchSize(Integer.MIN_VALUE)是大读数的最佳选择 – 2016-10-11 14:48:39

+0

@VladislavVaintroub这是一个折衷。其实我可能需要更新这个答案,因为我相信MySQL做了一些改变,使用正确的配置,你实际上可以使用除1或全部之外的获取大小。 – 2016-10-11 14:52:43

11

如果启用MySQL JDBC optionuseCursorFetch,fetchSize的确会受到驱动程序的重视。

但是,这种方法有一个缺点:它将使用服务器端游标,它在MySQL中使用临时表来实现。这意味着只有在服务器上完成查询之后才能得出结果,并且服务器端会使用额外的内存。

如果您只是想使用结果流并且不关心确切的获取大小,那么setFetchSize(Integer.MIN_VALUE)的开销并不像文档可能暗示的那样糟糕。它实际上只是禁用整个响应的客户端缓存,并在您到达时给予响应;每行都不需要往返。

+0

很好的答案。你能否指出任何文件来支持你的断言,即不需要往返旅行?似乎对我来说,无论如何都需要某种背景线程。 – rogerdpack 2016-07-01 22:20:38

+1

对于MIN_VALUE,MySQL似乎依赖于底层TCP套接字的流量控制。只要接收窗口的大小合理(并且所有现代操作系统默认都是这样做的),除非首先读取的带宽有限,否则不应该等待更多数据。 – lxgr 2016-07-05 20:43:13

+0

当你说客户端缓存独自被禁用我假设查询的所有结果仍然一次加载到内存中,并且客户端一次只获取一个。这样对吗? – mns 2016-11-25 09:05:14

1

这不是上述问题的答案。由于我不能评论它,所以我去提供它作为答案。对于一些面临类似问题的人来说,这可能会有帮助

对于批处理作业,由于结果集太大,我需要打开流模式。首先,如在MySQL doc看到,设置我的连接起来是这样的:

Statement extrapackStreamingQuery = dbExtrapackConnection.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); 
extrapackStreamingQuery.setFetchSize(Integer.MIN_VALUE); 

但是,如果系统给我的错误:

Application was streaming results when the connection failed. Consider raising value of 'net_write_timeout' on the server. 

我也尝试了几个配置选项,如:max_allowed_packet = 128M,max_connect_errors = 9999net_write_timeout = 180。但他们都没有帮助。

错误地认为TCP连接可能由于空闲时间太长而关闭,我甚至尝试在​​和/etc/sysctl.conf文件中用net.ipv4.tcp_keepalive_time=60更改TCP ping时间帧。事实上,如果打开数据库连接但没有足够长的TCP数据包发送时间,那么当TCP连接关闭时,数据库连接将会丢失。更频繁地发送TCP数据包以保持TCP连接处于活动状态可能会解决此问题。

但是这也没有帮助。

然后,阅读本piece后,我改变我的连接设置到:

protected static final int DB_STREAMING_FETCH_AMOUNT = 50; 
... 
Statement extrapackStreamingQuery = dbExtrapackConnection.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY);  
extrapackStreamingQuery.setFetchSize(DB_STREAMING_FETCH_AMOUNT); 

使用尾随选择我的网址:

String fullUrl = url + host + ":" + port + "/" + dbName; 
if (streaming) { 
    fullUrl += "?useCursorFetch=true"; 
} 

我的批处理作业现在工作正常,它完成,然后甚至跑得更快。

相关问题