构造 Jooq 流太慢
Posted
技术标签:
【中文标题】构造 Jooq 流太慢【英文标题】:construct Jooq stream is too slow 【发布时间】:2017-11-23 20:50:39 【问题描述】:我正在使用 Scala。
我试图从一个大约有 400 万行的表中获取所有数据。我使用流,代码是这样的
val stream Stream[Record] = expression.stream().iterator().asScala.toStream
stream.map(println(_))
表达式是 Jooq 中的 SelectFinalStep[Record]。
但是,第一行太慢了。它需要几分钟。我做错了吗?
【问题讨论】:
流保存在内存中以便重新遍历。如果您只打算使用一次,则应该直接使用迭代器。您应该考虑使用更现代的流式处理框架,或使用 Slick 流式处理操作来使用数据库。我很确定在该表达式上调用stream
试图获取太多的数据库行,您需要一个分页系统,它可以让您“及时”获取结果集的块,而不是提前全部获取,并指定提取大小,因此您在任何给定时间只处理 10k 条记录,比方说,而不是 400 万条。
@flavian:当 jOOQ 可以很容易地做到这一点时,为什么还要推荐 slick?
我正在使用流以避免使用过多的内存。如您所说,它不会节省任何内存。 @flavian 有什么存档建议吗?
【参考方案1】:
直接使用 Stream API
如果您使用的是 Scala 2.12,则不必将 expression.stream()
返回的 Java 流转换为 Scala Iterator
,然后再转换为 Scala Stream
。只需调用:
expression.stream().forEach(println);
虽然 jOOQ 的 ResultQuery.stream()
方法创建了一个惰性 Java 8 Stream
,它在使用后再次被丢弃,但 Scala 的 Stream
将先前获取的记录保留在内存中以供重新遍历。在获取 400 万条记录时,这可能是导致大多数性能问题的原因。
资源说明
请注意expression.stream()
返回一个资源丰富的流,保持开放的底层ResultSet
和PreparedStatement
。也许,在消费后显式关闭流是个好主意。
优化 JDBC 提取大小
另外,您可能想考虑调用expression.fetchSize()
,它会调用JDBC 的Statement.setFetchSize()
。这允许 JDBC 驱动程序获取 N 行的批次。一些 JDBC 驱动程序默认为合理的提取大小,其他默认为在将所有行传递给客户端之前将它们提取到内存中。
【讨论】:
我正在使用流以避免使用过多的内存。如您所说,它不会节省任何内存。有什么建议存档吗?我还尝试使用 ResultQuery.fetchlazy 和 ResultQuery.fetchsize。 ResultQuery.fetchlazy 也很慢。 @LongxingWei: 1) Java 流!= Scala 流。 2)您可以编辑您的问题并发布您的整个查询吗?如果fetchLazy()
仍然很慢,那么可能只是调整 SQL 的问题【参考方案2】:
另一种解决方案是懒惰地获取记录并构造 a scala 流。例如:
def allRecords():Stream[Record] =
val cur = expression.fetchLazy()
def inner(): Stream[Record] =
if(cur.hasNext)
val next = cur.fetchOne
next #:: inner()
else
Stream.empty
inner()
【讨论】:
以上是关于构造 Jooq 流太慢的主要内容,如果未能解决你的问题,请参考以下文章
jooq multiset jsonb 列失败,无法构造 `org.jooq.JSONB` 的实例