需要帮助改善grails中大型数据集的性能

时间:2010-09-16 19:35:24

标签: grails gorm large-data

此解决方案有效,但性能低于预期。返回200K行的查询需要几分钟,并在我的开发框上固定CPU。在查询分析器中运行相同的*查询将返回< 1分钟。

Class MyController { 

 def index = {...}
 ...
 def csv = {
   ...
   def rs = DomainClass.createCritera().scroll {}

   while(rs.next()){
    response.getOutputStream().print(rs.getString(1)\n)
   }
   ...
 }

DB =与我的开发机器分开的专用盒子上的SQL Server 2005服务器。

我还注意到,通过SQL Server Profiler,gorm / hibernate使用sp_cursorprepexec和sp_cursorfetch一次读取结果128行。如果它是一个选项,我想尝试不使用光标。

不确定是否是问题,但只能提供帮助。在休眠时,可以将滚动设置为仅向前但我无法找到类似的grails设置。

原始的休眠issue

解决方案:绕过休眠。从10分钟到15秒。

Class MyController { 
 def DataSource

 def index = {...}
 ...
 def csv = {
   ...
   def out = response.getOutoutStream()
   Sql sql = new Sql(dataSource)

   sql.eachRow("select c1, c2 from t1",{
     out.println( it.c1 + "," + it.c2 )
   })
   ...
 }

* same =从SQL Server Profiler剪切和粘贴,但不包括sp_cursorprepexec sproc。

5 个答案:

答案 0 :(得分:4)

如果GORM不支持某些内容,可以直接下载到Hibernate:

import org.hibernate.ScrollMode

class MyController { 

   def index = {...}

   def csv = {
      DomainClass.withSession { session ->
         def rs = session.createCriteria(DomainClass).scroll(ScrollMode.FORWARD_ONLY)
         while (rs.next()) {
            response.outputStream.print rs.getString(1)
         }
      }
   }
}

您可以使用session.createQuery(...)代替HQL查询。

答案 1 :(得分:3)

Hibernate并不是真正用于批处理加载,但有一些事情你可以尝试(其中大部分都要求你放弃ScrollableResult用法,只是用对象结果进行常规查询)。

  1. 只需绕过Hibernate / GORM,直接转到SQL(希望)少数几个你需要它的地方。雅,我知道,但如果情况变得更糟......
  2. 调用session.setReadOnly()或query.setReadOnly()以禁用Hibernate的状态快照
  3. 试试Hibernate的无状态会话。如果您所做的只是阅读,这可能会正常。无状态会话的开销远低于常规Hibernate会话,但您将放弃所有缓存和对象状态跟踪。你必须做这样的事情才能使用它:

    def Session statelessSession = sessionFactory.openStatelessSession()
    statelessSession.beginTransaction()
    
    // ...
    
    statelessSession.getTransaction().commit()
    statelessSession.close()
    
  4. 以25或50的批量刷新会话。实际上,当您迭代已经带回的项目时,请执行session.flush()。如果你不这样做,会话将继续增长,直到你的内存不足,你的垃圾收集器开始变得疯狂。这可能就是您的处理器被挂钩的原因。

  5. 祝你好运!

答案 2 :(得分:0)

使用Grails条件和ScrollMode的另一种方法:

Criteria criteria = Domain.createCriteria().buildCriteria{
    eq('id', id)
}
ScrollableResults results = criteria.scroll(ScrollMode.FORWARD_ONLY)

int i = 0
while (results.next()){
    ...
    if (++i % 50 == 0){
        Domain.withSession { Session session ->
            session.flush()
            session.clear()
        }
    }
}

答案 3 :(得分:0)

值得注意的一些事情:

答案 4 :(得分:0)

使用批量插入,它比gorm清理方法和无状态会话方法更快。下面的例子可以帮助您实现如何在grails中实现批量插入。

    Date startTime   = new Date()
    Session session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();

    (1..50000).each {counter ->
        Person person           = new Person()
        person.firstName        = "abc"
        person.middleName       = "abc"
        person.lastName         = "abc"
        person.address          = "abc"
        person.favouriteGame    = "abc"
        person.favouriteActor   = "abc"

        session.save(person)
        if(counter.mod(100)==0) {
            session.flush();
            session.clear();
        }

        if(counter.mod(10000)==0) {
            Date endTime    =new Date()
            println "Total record insert Counter =>"+counter+" Time =>"+TimeCategory.minus(endTime,startTime)
        }
    }

    tx.commit();
    session.close();