2012-01-09 59 views
0

我需要使用自定义的ElasticSearch实现定期重新为实时遗留数据库中的所有域实例重新编制索引。Grails批量读取优化

到目前为止,我发现的所有建议都是针对优化批次编写操作。

许多表格都有成千上万的数据(如果不是数百万条记录的话),并且简单的解决方案domain.list().each { ... }似乎一次将整个数据集加载到内存中。

使用休眠ScrollableResults似乎是一个很好的解决方案,但它的工作原理在RecordSet级别,并且不会重新创建实际的域对象。我可以使用id字段到read()实例,但这似乎效率低下。

Searchable插件似乎执行有效的reindex操作。

还有哪些其他选项或优化可以成功批量读取域实例?


UPDATE

我最近碰到一个4岁的线程跌跌撞撞Grails的邮件列表上有answer@BurtBeckwith那建议使用ScrollableResultSet是低效的。他建议使用标准GORM方法对记录进行分页,并分批清除会话。

回答

1

1)使用传统的Java for循环会节省一些开销。

2)分块数据可能有帮助。您可以使用max:和offset:来获得大小为100或1000的方式,这样您总是可以处理更小的集合:http://grails.org/doc/latest/ref/Domain%20Classes/list.html 虽然如果您要修改该表中的对象或者新数据在工作流程的中间添加。 (我会让你测试:)

3)使用SQL或HSQL获取所有的ID,然后使用Grails .load()也可以。 http://grails.org/doc/latest/ref/Domain%20Classes/load.html

4)我已经尝试过使用Grails/Hibernate实现一些大型批处理作业,并发现它实际上效果不佳。如果可能的话,我建议你直接使用SQL来处理数据库。这将是最快的。

+0

我认为你有一个ScrollableResults查询所有实例ID的正确的想法,然后单独加载它们。唯一的问题是具有复合主键的遗留表。 – jamesallman 2012-01-09 20:04:07

+0

根据您的数据库实现,使用偏移量可能会增加大量的开销,因为您反复选择相同的数据。这对于简单的查询可能不是问题,但可以为更复杂的查询添加大量时间。 – Joseph 2014-02-26 14:27:31

0

您可以分批

class Paginator { 
    private static final Logger log = Logger.getLogger("grails.app.Paginator") 

    int batchSize 
    int totalCount 

    public void eachPage(Closure c) { 
     SessionFactory sessionFactory = Holders.applicationContext.sessionFactory 

     if(totalCount > 0) { 
      (0..totalCount -1).step(batchSize) { int offset -> 
       log.debug "Executing batch $offset to ${offset + batchSize}" 
       try { 
        c.call(batchSize, offset) 
        sessionFactory.currentSession.clear() 
       }catch(Exception e) { 
        log.error "Error encountered while executing batch [$offset - ${batchSize + offset}]" 
       } 
      } 
     } 
    } 
} 

然后

Paginator p = new Paginator(batchSize:500, totalCount: Domain.count()) 
p.eachPage {def max, def offset -> 
    List rows = Domain.list(max:max, offset:offset) 
    //do some thing with records 
} 

您可以使用格姆或直接SQL或任何的分页程序使用类似下面的一个实用工具类手动负载记录。