【问题标题】:Is there a mysql JDBC that will respect fetchSize?是否有尊重 fetchSize 的 mysql JDBC?
【发布时间】:2014-11-20 16:16:41
【问题描述】:

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

我想插入一个 JDBC 实现,该实现将与 MySQL 一起工作并正确遵守 fetch size,允许我将 fetchsize 设置为 10,000 或其他更高的限制。谁能指点我一个可以提供这种实现的罐子?如果没有任何其他资源允许我以一种高效的方式合理地执行包含数万个条目的查询,但在内存和所需的 sql 查询数量方面。

【问题讨论】:

  • Integer.MIN_VALUE 是负数,它如何获取一些负数的行?
  • @ElliottFrisch 这就是 MySQL JDBC 驱动程序的工作方式。当您指定Integer.MIN_VALUE 时,它要么获取(流)所有内容,要么允许一对一检索。见dev.mysql.com/doc/connector-j/en/…(在结果集下)
  • “必须每行执行一个查询” 我是否理解您认为 fetchSize 控制检索到的最大行数?因为这不是发生的情况:单个查询仍会产生所有行,它只会逐个获取行。
  • @mark 你是对的,这只是一个查询。必须为每行 ping 服务器的延迟和带宽成本仍然是我关心的问题。抱歉我的描述不够准确。
  • 每行确实没有 ping 或往返(正如 MySQL 文档的不幸措辞可能暗示的那样)。

标签: java mysql sql jdbc


【解决方案1】:

如果启用MySQL JDBC optionuseCursorFetch,驱动程序确实会尊重fetchSize。

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

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

【讨论】:

  • 好答案。您能否指出任何文档来支持您的断言,即不需要往返?在我看来,无论如何都需要某种后台线程..
  • 使用 MIN_VALUE,MySQL 似乎依赖于底层 TCP 套接字的流控制。只要接收窗口的大小合理(所有现代操作系统都默认这样做),您就不必等待更多数据,除非您的读取首先受到带宽限制。
  • 当您说仅禁用客户端缓存时,我假设查询的所有结果仍然一次加载到内存中,并且客户端一次只获取一个。对吗?
  • 不,结果应该真正到达,因为它们是由服务器流式传输的。我用一个需要几分钟才能完成的长时间运行的查询对此进行了测试:使用 MIN_VALUE,结果在更短的初始等待后开始出现。
【解决方案2】:

询问图书馆的技术问题是题外话。也就是说,据我所知,MySQL 没有替代驱动程序。您可以选择获取所有可能导致内存不足的行,或者通过设置 setFetchSize(Integer.MIN_VALUE) 让驱动程序按需获取它们。

原因——据我从Connector/J implementation notes 了解到——是 MySQL 协议每个连接不能打开多个游标,因此它默认在执行时将所有行流式传输到客户端。

另一种选择是逐一检索行,但这会带来一个问题,即在处理ResultSet时,您无法在同一连接上执行其他语句:

这种方法有一些注意事项。您必须阅读结果集中的所有行(或将其关闭)您可以在连接上发出任何其他查询,或者会抛出异常。

所以 MySQL 只能选择获取所有内容或一次获取一个。这意味着驱动程序无法遵守不同的提取大小。由于一对一的注意事项,他们选择使用Integer.MIN_VALUE(而不是简单的1)作为一个信号,你应该在这样做之前真正考虑一下。

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

【讨论】:

  • 谢谢。我认为这很有可能是对数据库本身的限制,但我感谢您确认。然而,我很惊讶有人没有制作一个设置 LIMIT 和 OFFSET 的包装器,并在幕后为您进行查询以模仿 setFetchSize 预期的行为。我知道这不是一回事,但可以在 javadoc 中记录这种包装器的区别和优势/权衡。
  • 使用 LIMIT/OFFSET 时,您将多次执行查询,可能会给您错误的结果(重复行,跳过其他行),除非您 LOCK TABLES for READ 直到 ResultSet 关闭,然后解锁它们。相当昂贵,实现并不容易(需要解析查询以了解涉及哪些表),所以这不是一个好主意。或者,您可以 CREATE TEMPORARY TABLE AS SELECT...,并使用 LIMIT/OFFSET 进行读取,但是这很容易出现幻读,而且价格昂贵。 setFetchSize(Integer.MIN_VALUE) 是大读取的最佳选择
  • @VladislavVaintroub 这是一个权衡。实际上我可能需要更新这个答案,因为我相信 MySQL 进行了一些更改,并且通过正确的配置,您实际上可以使用 1 或 all 以外的 fetch size。
  • 我用setFetchSize(Integer.MIN_VALUE) 测试了基本示例,与官方文档相反,它不是“逐行”完成的,而是以流方式完成的。我测试了一个返回 1841 个整数的查询只使用了 40 个 TCP 数据包(这包括驱动程序完成的“准备查询”)。
  • 使用OFFSET会导致它自己的问题,可能比原问题中的问题更严重:use-the-index-luke.com/no-offset
【解决方案3】:

这并不是上述问题的真正答案。由于我无法将其放入评论中,因此我将其作为答案提供。它可能对一些面临类似问题的人有所帮助。

对于批处理作业,我需要打开流模式,因为我的结果集太大。起初,如 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 = 128Mmax_connect_errors = 9999net_write_timeout = 180。但他们都没有帮助。

错误地认为 TCP 连接可能因空闲时间过长而关闭,我什至尝试在 /proc/sys/net/ipv4/tcp_keepalive_time/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";
}

我的批处理作业现在运行良好,它可以完成,甚至运行得更快。

【讨论】:

    猜你喜欢
    • 2018-02-24
    • 2016-12-16
    • 2013-08-18
    • 2021-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-06
    • 1970-01-01
    相关资源
    最近更新 更多