当前位置: 首页 > 面试题库 >

Grails,使用withTransaction插入大量数据会导致OutOfMemoryError

岳风畔
2023-03-14
问题内容

我正在使用Grails 1.1
beta2。我需要将大量数据导入Grails应用程序。如果我反复实例化grails域类然后保存它,则性能会降低到无法接受的程度。以从电话簿导入人为例:

for (each person in legacy phone book) {
    // Construct new Grails domain class from legacy phone book person
    Person person = new Person(...)
    person.save()
}

事实证明这是缓慢的。Grails邮件列表上的某人建议在事务中分批保存。所以现在我有:

List batch = new ArrayList()
for (each person in legacy phone book) {
    // Construct new Grails domain class from legacy phone book person
    Person person = new Person(...)
    batch.add(person)
    if (batch.size() > 500) {
        Person.withTransaction {
            for (Person p: batch)
                p.save()
            batch.clear()
        }
    }
}
// Save any remaining
for (Person p: batch)
    p.save()

这必须至少在开始时更快。每笔交易会保存500条记录。随着时间的流逝,交易花费的时间越来越长。最初的几笔交易大约需要5秒钟,然后才从那里开始。经过约100笔交易,每笔交易都要花一分钟多的时间,这再次令人无法接受。更糟糕的是,最终Grails最终将耗尽Java堆内存。我可以增加JVM堆大小,但这只会延迟OutOfMemoryError异常。

任何想法为什么会这样?就像有一些内部资源没有被释放。性能变得更糟,内存被保留,然后最终系统内存不足。

根据Grails文档,withTransaction将闭包传递给Spring的TransactionStatus对象。我找不到任何东西TransactionStatus来完成/结束交易。

编辑: 我正在从Grails的控制台(grails console)运行此文件

编辑: 这是内存不足异常:

Exception thrown: Java heap space

java.lang.OutOfMemoryError: Java heap space
    at org.hibernate.util.IdentityMap.entryArray(IdentityMap.java:194)
    at org.hibernate.util.IdentityMap.concurrentEntries(IdentityMap.java:59)
    at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:113)
    at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
    at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:655)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:732)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:701)
    at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:140)

问题答案:

这是所有hibernate应用程序的常见问题,它是由hibernate会话的增长引起的。我猜想grails控制台会为您打开一个hibernate会话,其方式类似于我知道它用于普通Web请求的“视图中打开会话”模式。

解决方案是掌握当前会话,并在每次批处理后清除它。我不确定如何使用控制台来获取spring
bean,通常对于控制器或服务,只需将它们声明为成员即可。然后,您可以使用获取当前会话sessionFactory.getCurrentSession()。为了清除它,只需调用session.clear(),或者session.evict(Object)对每个Person对象有选择地使用。

对于控制器/服务:

class FooController {
    def sessionFactory

    def doStuff = {
        List batch = new ArrayList()
        for (each person in legacy phone book) {
            // Construct new Grails domain class from legacy phone book person
            Person person = new Person(...)
            batch.add(person)
            if (batch.size() > 500) {
                Person.withTransaction {
                    for (Person p: batch)
                        p.save()
                    batch.clear()
                }
                // clear session here.
                sessionFactory.getCurrentSession().clear();
            }
        }
        // Save any remaining
        for (Person p: batch)
            p.save()
        }
    }
}

希望这可以帮助。



 类似资料:
  • 问题内容: 我读到这里:使用Python将CSV文件导入sqlite3数据库表 似乎每个人都建议使用逐行读取,而不是使用SQLite的bulk .import。但是,如果您有数百万行的数据,这将使插入速度非常慢。还有其他方法可以避免这种情况吗? 更新:我尝试下面的代码逐行插入,但是速度不如我预期的那样。反正有改进的地方吗 问题答案: 使用生成器表达式将您的数据即时划分为多个块,在事务内进行插入。这

  • 我正在创建一个int数组SecretNumber。当我将数组大小声明为数字时,没有越界异常,但当我用变量(numDigits)声明数组大小时,在索引0处得到行“secretNumber[I]=val”的越界异常。这是类: 这是执行这些方法的主体,我在设置数组之前运行numDigits setter: 如果我设置了NumDigits,为什么索引0会超出界限??

  • 我创建了一个向MySql数据库插入数百万个值的程序。我读到过有关批插入的文章,它将优化我的程序并使其更快,但当我尝试这样做时,它以同样的方式工作。我没有将每个值插入数据库,而是每次将500个值保存在一个列表中,然后将它们插入一个大循环中,如下所示: 然后我删除列表中的所有值,并再次开始收集500个值。它不应该工作得更好吗? 我的插入代码是: 我有一些问题: 1。为什么当我批量插入时它不能更快地工作

  • 我在我的Tomcat/Java应用程序中有一个解析代码的GroovyShell实例。解析非常慢,100行大约1秒。在分析应用程序时,我注意到解析抛出了大量java.lang.ClassNotFoundException异常。我想有什么东西在吞噬它们,因为我在原木的任何地方都没有看到它们。由于脚本使用了大量主应用程序的类,所以我假设这就是导致应用程序变慢的原因。 有没有办法让我捕捉那些异常并获取它们

  • 首先是一些背景知识:我正在使用Spring Security的修改版本来执行Active Directory身份验证,并检查存储在数据库中的可能访问权限。这意味着在普通的Groovy类中有一个调用来从数据库中加载信息: 这在Hibernate 4.3.6.1和Tomcat 7.0.54上效果很好,但是,在升级(升级到4.3.10.18和8.0.14.1)后,它现在在调用动态查找器方法时会产生“Hi

  • websocket会实时发送大量数据时,我目前写的这些代码,会导致页面卡顿,性能优化我不知道怎么下手了,求大佬指教一下, 有偿。谢谢大佬们