5

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

常见的解决方案是对每个 n (egn=500) 个对象刷新() 和 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(),那么 currentSession 仍然在 FooService 中使用。当然,我可以使用 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) 的操作。两者都不存在!

任何想法都是受欢迎的!

4

2 回答 2

1

我建议对这种批处理使用无状态会话。请参阅这篇文章:使用 StatelessSession 进行批处理

于 2015-09-30T10:02:08.130 回答
0

修改后的方法是:

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

然后像现在一样使用相同的定期清除会话缓存。

顺便说一句,其他人提出了与您类似的方法。但请注意,此链接中的代码不正确;清除会话的行应该在 if 语句中,就像您在解决方案中一样。

于 2017-02-21T23:15:47.467 回答