构造 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() 返回一个资源丰富的流,保持开放的底层ResultSetPreparedStatement。也许,在消费后显式关闭流是个好主意。

优化 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 中,为啥连接与语句构造高度耦合?

jooq multiset jsonb 列失败,无法构造 `org.jooq.JSONB` 的实例

如何使用默认构造函数和设置器生成 pojo

jooq, simpleflatmapper, kotlin 映射

如何在没有域类的 querydsl 中构造查询

CsvHelper 流太长