【问题标题】:Grails hibernate session in batchesGrails 分批休眠会话
【发布时间】:2017-02-22 15:21:57
【问题描述】:

只要没有超过 10.000 个对象的批次,GORM 开箱即用就可以正常工作。如果不进行优化,您将面临 outOfMemory 问题。

常见的解决方案是对会话中的每个 n(例如 n=500)个对象进行 flush() 和 clear():

Session session = sessionFactory.currentSession
Transaction tx = session.beginTransaction();
def propertyInstanceMap = org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin.PROPERTY_INSTANCE_MAP

Date yesterday = new Date() - 1

Criteria c = session.createCriteria(Foo.class)
c.add(Restrictions.lt('lastUpdated',yesterday))
ScrollableResults rawObjects = c.scroll(ScrollMode.FORWARD_ONLY)

int count=0;
while ( rawObjects.next() ) {
    def rawOject = rawObjects.get(0);

    fooService.doSomething()

    int batchSize = 500
    if ( ++count % batchSize == 0 ) {
        //flush a batch of updates and release memory:
        try{
            session.flush();
        }catch(Exception e){
            log.error(session)
            log.error(" error: " + e.message)
            throw e
        }
        session.clear();
        propertyInstanceMap.get().clear()
    }
}

session.flush()
session.clear()
tx.commit()

但是有一些问题我解决不了:

  1. 如果我使用 currentSession,那么控制器会因为会话为空而失败
  2. 如果我使用 sessionFactory.openSession(),那么在 FooService 内部仍然使用 currentSession。当然,我可以使用 session.save(object) 表示法。但这意味着,我必须修改 fooService.doSomething() 并为单个操作(常见的 grails 表示法,如 fooObject.save() )和批处理操作(session.save(fooObject() ).. 表示法)修改代码。李>
  3. 如果我使用 Foo.withSession{session->} 或 Foo.withNewSession{session->},那么 Foo 类的对象会按预期由 session.clear() 清除。所有其他对象都没有清除(),导致内存泄漏。
  4. 当然,我可以使用 evict(object) 手动清除会话。但由于关联的自动获取,几乎不可能获取所有相关对象。

所以我不知道如何在不使 FooService.doSomething() 更复杂的情况下解决我的问题。我正在为所有域寻找类似 withSession{} 的东西。或者在开始时保存会话(Session tmp = currentSession)并执行类似 sessionFactory.setCurrentSession(tmp) 的操作。两者都不存在!

欢迎任何想法!

【问题讨论】:

  • 这看起来像是应该完全在服务方法中完成的工作。如果在服务方法中,你使用currentSession,你的控制器还能工作吗?
  • 我同意@doelleri。服务是做这件事的好地方。另外,请记住,默认情况下它们是事务性的,如果您想手动处理状态,请使用 Domain.withTransaction 并设置 static transactional = false 或让服务负责提交/回滚。
  • 我发布的代码已经在服务方法中。是的,我可以使用服务方法的事务上下文,但它不能解决我的问题。 @doelleri - 你的问题的答案是:控制器刹车,所以用户除了关闭浏览器之外不能在应用程序中做任何其他事情。 (见问题 1)
  • 看看Spring Batch。易于集成到 grails 中并且非常强大
  • 感谢您的建议!由于 Spring Batch 的复杂性,我想避免使用它。我已经集成了 quarz 作业,这意味着使用 Spring Batch 和 Quarz 在一个应用程序中将有两种作业调度方法。有没有更简单的解决方案?

标签: performance hibernate grails grails-orm


【解决方案1】:

我建议对这种批处理使用无状态会话。看到这个帖子:Using StatelessSession for Batch processing

【讨论】:

    【解决方案2】:

    修改后的方法是:

    1. 循环遍历您的整个集合 (rawObjects) 并保存这些对象的所有 ID 列表。
    2. 循环遍历 ID 列表。在每次迭代中,仅通过其 id 查找单个对象。

    然后像现在一样定期清除会话缓存。

    顺便说一句,someone else has suggested an approach similar to yours。但请注意,此链接中的代码不正确;清除会话的行应该在 if 语句中,就像您在解决方案中一样。

    【讨论】:

      猜你喜欢
      • 2015-08-07
      • 2013-05-18
      • 1970-01-01
      • 2012-06-02
      • 2018-09-27
      • 2013-09-14
      • 1970-01-01
      相关资源
      最近更新 更多