【问题标题】:ScrollableResults with Hibernate/Oracle pulling everything into memory使用 Hibernate/Oracle 将所有内容拉入内存的 ScrollableResults
【发布时间】:2021-01-04 14:22:38
【问题描述】:

我想要一页来自 Oracle 数据库表的过滤数据,但我有一个可能返回数千万条记录的查询,因此将其全部拉入内存是不可行的。我需要以无法通过 SQL 完成的方式过滤记录,并返回一页记录。换句话说,分页部分必须在过滤之后完成。

所以,我尝试使用 Hibernate 的 ScrollableResults,认为这将是一种一次只提取块并遍历它们的方法。所以,我创建了它:

ScrollableResults results = query.setReadOnly(true)
    .setFetchSize(500)
    .setCacheable(false)
    .scroll();

...然而,它似乎将所有内容都拉入内存(每个查询拉入 2.5GB)。我见过another question 并尝试了一些建议,但大多数似乎是特定于MySQL 的,而且我使用的是Oracle 19 驱动程序(例如Integer.MIN_VALUE 在Oracle 驱动程序中被完全拒绝为获取大小)。

有人建议使用无状态会话(我正在使用没有无状态选项的EntityManager),但我的想法是,如果我们不获取很多记录(因为我们只想要200 条过滤记录),为什么 Hibernate 会在内存中保留数百万条记录,即使我们从未滚动过它们?

我很清楚,我不明白 Hibernate 如何/为什么将事物拉入内存,或者如何让它停止这样做。鉴于上述限制,有关如何防止它这样做的任何建议?

我要尝试的一些事情:

  • 不同的滚动模式。也许麻木不仁或向前只是阻止 Hibernate 需要把所有东西都拉进去?
  • 获得页面后清除会话。我正在关闭会话(在ScrollableResultsEntityManager 中都使用close()),但也许明确的clear() 会有所帮助?

【问题讨论】:

  • 这些比 Hibernate 更依赖于数据库,因此您需要搜索 Oracle 特定问题。让 fetchsize 与 Postgres 一起正常工作也很棘手。
  • 如果这是一个耗费资源/时间的“繁重”查询,那么将所有内容抓取到内存中可能还不错。但是,如果您必须使用 Oracle 对每个页面重复查询,一种常见的方法是在查询中使用 ROW_NUMBER 根据分组条件对每一行进行编号。数据,然后过滤 ROW_NUMBER 的值介于两者之间与您的页面相对应的上记录编号。不利的一面是,如果基础数据发生更改以影响结果,您可能会发现您“跳过”或“重复”行。
  • 我一直调试到 JDBC 调用。 Oracle 驱动程序发回的ResultSet 可以适当地滚动,并且其中有一些标志,指示它是否已获取所有内容以及已获取多少记录。在调试器中很清楚驱动程序已经完成了它的工作。

标签: java oracle hibernate jpa entitymanager


【解决方案1】:

我们滚动浏览整个ScrollableResults 以获得总数。这导致了两件事:

  1. Hibernate 会话缓存的实体。
  2. 驱动程序中的ResultSet 保留了它滚动过去的行。

解决这个问题确实是针对我的情况,但我做了两件事:

  1. 当我们滚动时,定期清除休眠会话。由于我们使用EntityManager,所以我必须使用entityManager.unwrap(Session.class).clear()。不确定entityManager.clear() 是否能胜任这项工作。
  2. ScrollableResults 设为只进,这样Oracle 驱动程序就不必在滚动时将记录保存在内存中。这就像.scroll(ScrollMode.FORWARD_ONLY) 一样简单。不过,这才有可能,因为我们只是在前进。

这使我们能够保持更小的内存占用,即使在滚动浏览几乎每条记录(数千万条记录)时也是如此。

【讨论】:

    【解决方案2】:

    您为什么要滚动浏览所有结果来获取计数?为什么不直接执行计数查询?

    【讨论】:

    • 计数不能包括被过滤掉的记录。这种过滤不能在 SQL 中进行。
    • 你在做什么样的过滤,这在 SQL 中是做不到的?您也可以通过主键排序进行分页滚动,即使用键集分页。
    猜你喜欢
    • 2016-12-03
    • 1970-01-01
    • 2021-10-14
    • 2016-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多