【问题标题】:Grails batch read optimizationGrails 批量读取优化
【发布时间】:2012-02-06 06:31:57
【问题描述】:

我需要使用自定义 ElasticSearch 实现定期从实时遗留数据库中重新索引我的所有域实例。

目前我发现的所有建议都是针对优化批量写入操作。

许多表都有数十万条记录,domain.list().each { ... } 的简单解决方案似乎可以一次将整个数据集加载到内存中。

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

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

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


更新

我最近在 Grails 邮件列表中偶然发现了一个 4 年前的帖子,其中包含来自 @BurtBeckwithanswer,这表明使用 ScrollableResultSet 效率低下。他建议使用标准 GORM 方法对记录进行分页,并批量清除会话。

【问题讨论】:

    标签: hibernate grails grails-orm


    【解决方案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 直接使用数据库。这将是迄今为止最快的。

    【讨论】:

    • 我认为您对所有实例 id 的 ScrollableResults 查询有正确的想法,然后单独加载它们。唯一的问题是带有复合主键的旧表。
    • 根据您的数据库实现,使用偏移量会增加大量开销,因为您重复选择相同的数据。对于简单的查询,这可能不是问题,但可以为更复杂的查询增加大量时间。
    【解决方案2】:

    您可以使用类似于下面的实用程序类来批量手动加载记录

     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
    }
    

    您可以将分页器与 gorm 或直接 sql 或任何东西一起使用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-07
      • 2016-12-20
      • 1970-01-01
      • 2012-11-10
      • 2019-06-24
      • 2014-01-16
      • 1970-01-01
      相关资源
      最近更新 更多