Spring批处理JdbcCursorItemReader导致内存不足问题。

5

我正在使用Spring Batch框架进行数据迁移。我使用的读取器是JdbcCursorItemReader。我将块大小设置为500,并将读取器获取大小设置为1000。但是,当使用Spring Batch运行服务时,它似乎只是一次性读取所有数据并耗尽内存,然后抛出内存不足问题。以下是我如何定义读取器:

   private JdbcCursorItemReader<Map<String, Object>> buildItemReader(final DataSource dataSource, String tableName,String tenant) {
        String tenantName = tenantHelper.determineTenant(tableName);
        JdbcCursorItemReader<Map<String, Object>> itemReader = new JdbcCursorItemReader<>();
        itemReader.setDataSource(dataSource);
        itemReader.setSql("select * from " + tableName + " where " + tenantName + " ='" + tenant + "'");
        itemReader.setRowMapper(new ColumnMapRowMapper());
        itemReader.setFetchSize(100);
        return itemReader;
    }

此外,从Spring批处理文档这里可以看到,我们应该能够通过使用jdbcCursorItemReader来避免内存问题。


显然,查询将仅获取完整结果,因为您没有限制大小。我认为您需要Partitioner - soorapadman
嗨@soorapadman,Partitioner是指将进程分成更多步骤吗?由于某些原因,我无法使用该函数,因为我必须动态创建步骤。我还在考虑使用jdbcPagingItemReader。但是分页对我也不起作用,我也尝试了这个问题中描述的方式:https://stackoverflow.com/questions/55369572/spring-batch-jdbcpagingitemreader-paging-not-work - Ray
获取大小只是对于数据库驱动程序的提示,其实现取决于数据库产品。您使用哪种数据库? - Mahmoud Ben Hassine
我正在使用PostgreSQL数据库,并且我已经对jdbcCursorItemReader进行了调试,它按照fetch size和chunk size进行了读写。但我很困惑为什么它会占用所有内存并将所有数据加载到内存中。 - Ray
好的,谢谢更新。我已经添加了一个答案,希望能对您有所帮助。 - Mahmoud Ben Hassine
3个回答

5

您可以尝试使用JdbcPagingItemReader代替JdbcCursorItemReader,其中页面大小可以在配置时设置。


谢谢,我已经通过使用JdbcPagingItemReader解决了这个问题。 - Ray
请通过点击左侧帮助您最多的答案旁边的复选标记来关闭您的问题。 - Anirudh Simha

4

通过使用jdbcPagingItemReader找到了解决方法。导致游标读取器消耗大量内存的根本原因是它将所有数据读入内存,然后进行处理,这将被JVM视为一个大对象,并直接分配到老年代中。在整个过程完成之前,它无法被收集。


1
根据Postgresql文档,驱动程序一次性收集查询的所有结果。
您可能可以尝试通过将获取大小设置为0来关闭光标。正如上述文档中所解释的那样,还有其他约束条件,请确保您的代码满足所有这些条件。仅供参考,这类似于MySQL可能出现的情况,其中应将获取大小设置为Integer.MIN_VALUE以流式传输结果(请参见此处此处)。
希望能对您有所帮助。

1
嗨Ben,感谢您的回复。您说得对,我使用的游标读取器将一次性获取所有结果,这将形成一个大对象并直接进入旧代,这是我的问题根源。我尝试过配置提取大小,但结果证明对我没有用。最终,我通过使用分页读取器解决了这个问题。 - Ray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接