Grails hibernate会话分批

时间:2012-12-06 17:04:43

标签: performance hibernate grails gorm

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

常见的解决方案是每次n(例如n = 500)对象刷新()和清除()会话:

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 Class的对象会被session.clear()按预期清除。所有其他对象都没有被清除(),这会导致内存泄漏。
  4. 原因我可以使用evict(object)来手动清除会话。但由于自动提取相关信息,几乎不可能获得所有相关对象。
  5. 所以我不知道如何在不使FooService.doSomething()更复杂的情况下解决我的问题。我正在为所有域寻找类似withSession {}的东西。或者在开始时保存会话(Session tmp = currentSession)并执行类似sessionFactory.setCurrentSession(tmp)的操作。两者都不存在!

    任何想法都很好!

2 个答案:

答案 0 :(得分:1)

我建议使用无状态会话进行这种批处理。请参阅此帖子:Using StatelessSession for Batch processing

答案 1 :(得分:0)

您正在做的改进方法是:

  1. 遍历整个集合(rawObjects)并保存这些对象的所有ID列表。
  2. 遍历ID列表。在每次迭代中,通过其id查找该单个对象。
  3. 然后像现在一样使用相同的会话缓存定期清除。

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