真正流式传输大型 MySQL 结果集

Posted

技术标签:

【中文标题】真正流式传输大型 MySQL 结果集【英文标题】:Really streaming large MySQL result sets 【发布时间】:2016-06-23 01:05:27 【问题描述】:

有一个similar question about streaming large results,但答案只是指向文档,没有明确的答案出现。 我相信仅treating a full result set as a stream 在 jdbc 驱动端仍然会占用大量内存..

我想知道是否有任何明确的模式或最佳实践可以让它发挥作用,尤其是在 jdbc 驱动程序方面。

特别是我不知道为什么setFetchSize(Integer.MIN_VALUE) 是一个非常好的主意,因为如果这意味着每行都通过网络单独发送,这似乎远非最佳。

我相信像 jooq 和 slick 这样的库已经解决了这个问题......并且很好奇如何在有和没有它们的情况下完成它。

谢谢!

【问题讨论】:

P.S.在使用 slick 时,postgres 有一些涉及和特定的东西:***.com/questions/31340507/… 文档 (dev.mysql.com/doc/connector-j/en/…) 说要么所有内容都加载到内存中,要么逐一检索行。其他数据库驱动程序有更明智的方法。 不,我不是。但是,如果有其他选择,那就应该讨论它。这里还记录了 useCursorFetch 属性 (dev.mysql.com/doc/connector-j/en/…),但我不清楚它的确切含义,以及 fetchSize 的实际值是否用于任何用途。 最近的相关问题here 可能很有趣,特别是关于使用 MySQL 的 JDBC 方面。 为了记录(因为您用jooq 标记了这个问题),jOOQ 在这里没有也不应该帮助您。流式传输服务器网络协议的职责。 JDBC 是协议抽象 API。 jOOQ 是一种 SQL 语言抽象 API,因此不应干扰 JDBC。虽然 jOOQ 确实支持 java.util.stream.Stream 用于流式传输结果,但这种高级抽象并未假设记录是如何通过网络传输的。 【参考方案1】:

我想知道是否有任何明确的模式或最佳实践可以让它工作,尤其是在 jdbc 驱动程序方面。

最佳做法是不进行同步流式传输,而是获取中等大小的块。但是避免使用OFFSET(也可以是see)。如果您进行批处理,可以通过首先选择数据并将其推送到临时表中来促进这一点(即,首先将您想要的原始结果转换为表,然后从表中选择块......数据库在复制数据方面非常快内部)。

同步流通常无法扩展(也称为迭代器)。它不能很好地用于批处理,当然也不能用于处理大量客户端。这就是为什么驱动程序会发生变化并做很多不同的事情的原因,因为它是一个拉模型,因此很难弄清楚要加载多少资源。异步流(推送模型)可能会有所帮助,但不幸的是 JDBC 标准不支持异步流。

您可能会注意到,但这就是为什么围绕 JDBC 的许多包装器(例如 Spring JDBC)不返回 Iterators 的原因之一(此外还需要手动清理资源)。一些包装器提供了迭代器,但实际上它们只是将结果转换为列表。

您与 Scala 版本的链接相当令人不安,鉴于管理 ResultSet 的有状态性质,它被赞成...它非常不像 Scala...我不确定那些人是否知道他们必须使用迭代器或正确关闭连接/结果集,这需要大量的命令式编程。

虽然让数据库决定缓冲多少似乎效率低下,但请记住,大多数数据库连接在内存方面都非常繁重(至少在 postgres 上是这样)。因此,如果您需要很长时间进行流式传输并且拥有许多客户端,您将不得不创建更多连接并给数据库带来严重负担。更不用说默认缓冲区可能已经过高度优化(即客户端最终得到的结果集大小)。

最后,批处理块可以并行完成,这显然比同步管道更有效,并且在出现问题时重新启动(无需重新处理已处理的数据)。

【讨论】:

以上是关于真正流式传输大型 MySQL 结果集的主要内容,如果未能解决你的问题,请参考以下文章

Netezza 流式处理结果集

数据冗余 - 加入大型结果集

如何将一个非常大的 lucene 结果集连接到一个真正的大型 sql 表 [10 的百万行]

ASP.NET WebAPI和带有大型数据集的jQuery(json)

流式处理的一些概念 一:时间域、窗口化(翻译)

序列化/反序列化大型数据集