代码之家  ›  专栏  ›  技术社区  ›  jason saldo

需要帮助提高Grails中大型数据集的性能

  •  2
  • jason saldo  · 技术社区  · 14 年前

    此解决方案有效,但性能低于预期。返回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_CursorPrexec和sp_CursorFetch一次读取结果128行。如果光标是一个选项,我想尽量不要使用它。

    不确定是不是有问题,但只能帮上忙。在Hibernate中,可以只将滚动设置为前进,但我很难找到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 )
       })
       ...
     }
    

    *相同=从SQL Server事件探查器中剪切和粘贴,但不包括包装sp_cursorprexec存储过程。

    5 回复  |  直到 14 年前
        1
  •  3
  •   Javid Jamae    14 年前

    Hibernate并不适用于批量加载,但您可以尝试一些方法(其中大多数方法要求您放弃可滚动结果的使用,只对对象结果进行常规查询)。

    1. 只需绕过Hibernate/Gorm,直接转到SQL中(希望)您需要它的一些地方。是的,我知道,但如果情况恶化…
    2. 调用session.setreadonly()或query.setreadonly()以禁用休眠 状态快照
    3. 尝试Hibernate的无状态会话。如果你所做的只是阅读,这可能会很好。无状态会话的开销比常规的休眠会话低得多,但是您将放弃所有的缓存和对象状态跟踪。你必须这样做才能使用它:

      def Session statelessSession = sessionFactory.openStatelessSession()
      statelessSession.beginTransaction()
      
      // ...
      
      statelessSession.getTransaction().commit()
      statelessSession.close()
      
    4. 以25或50个批次刷新会话。本质上,当你在迭代你带回的项目时,做一个session.flush()。如果不这样做,会话将继续增长,直到内存耗尽,垃圾收集器开始疯狂。这可能就是为什么您的处理器被挂起的原因。

    祝你好运!

        2
  •  4
  •   James McMahon    11 年前

    如果GORM不支持某些内容,则可以直接下拉到休眠状态:

    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)
             }
          }
       }
    }
    

    对于hql查询,可以使用 session.createQuery(...) 相反。

        3
  •  0
  •   user1067920    12 年前

    使用Grails标准和滚动模式的另一种方法:

    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()
            }
        }
    }
    
        4
  •  0
  •   Douglas Mendes    9 年前

    一些值得注意的事情:

        5
  •  0
  •   Prakash Bhavnath    8 年前

    使用批插入,它比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();
    
    推荐文章