来自 PostgreSQL 的流行(具有获取大小)

Posted

技术标签:

【中文标题】来自 PostgreSQL 的流行(具有获取大小)【英文标题】:Stream rows from PostgreSQL (with fetch size) 【发布时间】:2019-09-20 23:42:28 【问题描述】:

我想从 PostgreSQL 11.2 流式传输结果,而不是一次将所有结果读取到内存中。我使用的是最新的稳定版 SpringBoot 2.1.4.RELEASE。

我阅读了如何在 mysql 中执行此操作的文章。 http://knes1.github.io/blog/2015/2015-10-19-streaming-mysql-results-using-java8-streams-and-spring-data.html 我还阅读了如何在 PostgreSQL 中执行此操作的文章: Java 8 JPA Repository Stream row-by-row in Postgresql

我有这样的存储库:

public interface ProductRepository extends JpaRepository<Product, UUID> 
    @Query("SELECT p from Product p")
    @QueryHints(value = @QueryHint(name = HINT_FETCH_SIZE, value = "50"))
    Stream<Product> streamAll();

比我这样使用流:

  productRepository.streamAll().forEach(product -> export(product));

为了使示例更简单,'export' 方法是完全空的。

当我调用该方法时,我看到了 Hibernate 查询

Hibernate: select product0_.id as id1_0_, product0_.created as created2_0_, product0_.description as descript3_0_, product0_.name as name4_0_, product0_.product_type_id as product_5_0_ from products product0_ order by product0_.id

一段时间后,我遇到了 OutOfMemoryError。 查询提示没有帮助。

如何使用 Spring Boot 存储库(甚至 EntityManager)读取数据并以最佳方式从 DB 加载行。 我知道我可以进行分页,但是就像文章中写的那样,这不是最优化的方式。

【问题讨论】:

你用流做什么?您可能需要从中收集所有数据吗?那么问题与此有关吗? 使用该流发布方法的代码。 在帖子中我展示了我如何使用流。 这似乎是正确的。所以这取决于你在导出方法中做了什么。或者它取决于 JpaRepository 的实现。你也可以发布导出方法吗? 我在帖子里写的,导出方法有空体。什么也没做。我收到 OutOfMemoryError 是因为我一次阅读了所有内容。如何逐段阅读? 【参考方案1】:

目前使用 spring 检索所有数据,并且 Stream 仅应用于内存中的数据。

如果您查看org.springframework.data.jpa.provider.PersistenceProvider 的来源,它似乎使用ScrollableResults 来流式传输数据。

一般是ScrollableResults检索内存中的所有数据。

您可以使用 MySql 数据库 here 找到有趣的完整分析,但对于 Postgres 数据库可能同样适用。

如果您认为使用实际上不需要使用大量内存的解决方案也是如此,因为底层实现并未使用最佳实现。

【讨论】:

可能您没有仔细阅读我的问题,因为我添加了指向与您相同的文章的链接。我的问题的重点是,在这篇文章中,我不会将所有数据读取到内存中,但是当我为 PostgreSQL 执行此操作时,它会读取所有数据。【参考方案2】:

您必须在工作完成后分离实体。

import javax.persistence.EntityManager;
...
@Autowired
private EntityManager entityManager;
... 
// Your business logic
productRepository.streamAll().forEach(product -> 
   export(product);
   // must detach so that garbage collector can reclaim the memory.
   entityManager.detach(product);
);

【讨论】:

【参考方案3】:

我遇到了完全相同的问题,在对 spring data 和 hibernate 内部进行长时间调试后,我找到了适合我的解决方案。

所以要在 PostgreSQL 中使用游标获取数据,它应该是带有 Stream 结果 + 注释的方法(kotlin 语法):

@QueryHints(QueryHint(name = org.hibernate.annotations.QueryHints.FETCH_SIZE, value = "50"))

它应该是 50 还是其他值 - 这并不重要。 可能您输入了错误的提示名称。

【讨论】:

以上是关于来自 PostgreSQL 的流行(具有获取大小)的主要内容,如果未能解决你的问题,请参考以下文章

在PostgreSQL查询中获取大对象的大小?

如何获取 PostgreSQL jsonb 字段的大小?

sql 查询以获取PostgreSQL数据库的大小(公共模式)

Postgresql获取具有多列的每个组的最大值[重复]

如何在 PostgreSQL 9.5 中为“int”数据类型设置大小限制

如何在 PostgreSQL 9.5 中为“int”数据类型设置大小限制