【问题标题】:Grails save not respect flush optionGrails 保存不尊重刷新选项
【发布时间】:2012-11-25 20:17:33
【问题描述】:

我将 grails 用作穷人的 etl 工具,用于将一些相对较小的 db 对象从 1 db 迁移到下一个 db。我有一个控制器,它从一个数据库(mysql)读取数据并将其写入另一个数据库(pgsql)。它们使用相似的域对象,但由于 grails 2.1.X 中对多数据源支持的限制,它们并不完全相同。

您将在下面看到我的控制器和服务代码:

class GeoETLController {

    def zipcodeService

      def migrateZipCode() {
        def zc = zipcodeService.readMysql();
        zipcodeService.writePgSql(zc);

        render{["success":true] as JSON}
    }
}

还有服务:

class ZipcodeService {

    def sessionFactory
    def propertyInstanceMap = org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin.PROPERTY_INSTANCE_MAP

    def readMysql() {
        def zipcode_mysql = Zipcode.list();
        println("read, " + zipcode_mysql.size());
        return zipcode_mysql;
    }

    def writePgSql(zipcodes) {

        List<PGZipcode> zips = new ArrayList<PGZipcode>();
        println("attempting to save, " + zipcodes.size());
        def cntr = 0;
        zipcodes.each({ Zipcode zipcode ->
            cntr++;

            def props = zipcode.properties;
            PGZipcode zipcode_pg = new PGZipcode(zipcode.properties);

            if (!zipcode_pg.save(flush:false)) {
                zipcode_pg.errors.each {
                    println it
                }
            }
            zips.add(zipcode_pg)
            if (zips.size() % 100 == 0) {
                println("gorm begin" + new Date());
                // clear session here.
                this.cleanUpGorm();
                println("gorm complete" + new Date());

            }

        });
        //Save remaining
        this.cleanUpGorm();
        println("Final ." + new Date());
    }

    def cleanUpGorm() {
        def session = sessionFactory.currentSession
        session.flush()
        session.clear()
        propertyInstanceMap.get().clear()
    }
}

其中大部分来自我自己的代码,然后进行了调整以尝试获得与http://naleid.com/blog/2009/10/01/batch-import-performance-with-grails-and-mysql/ 中所见相似的性能

因此,在查看我的代码时,每当调用 zipcode_pg.save() 时,都会创建一个插入语句并将其发送到数据库。有利于数据库一致性,不利于批量操作。

我的即时刷新的原因是什么(注意:我的数据源和 congig groovy 文件没有相关更改)?以这个速度,处理每批 100 个(每秒 14 次插入)大约需要 7 秒,而当您处理 10,000 行时,这只是很长的时间......

感谢您的建议。

注意:我考虑过使用纯 ETL 工具,但是已经构建了如此多的域和服务逻辑,认为使用 grails 将是资源的良好重用。但是,没想到批量操作的质量如此

【问题讨论】:

  • 您是否分析过您的代码以验证数据库交互是否是瓶颈?我问是因为您的代码看起来不错。我已经看到了一些与一起使用 validate 和 flush 选项相关的错误,但不仅仅是单独使用 flush。每秒 14 次插入似乎非常非常慢。
  • 我在“调试模式”下运行了我的代码,以确保它实际上是在每次保存时发生的插入语句(也就是从 db pre-save 运行一个 select count(*) () 然后 post-save() 并看到 n+1)。我还使用了时序语句来确保它是缓慢的保存部分。我想瓶颈可能在数据库端,但即使是性能不佳的数据库也不应该看到那种水平的性能。

标签: postgresql grails grails-orm


【解决方案1】:

没有看到您的域对象,这只是一种预感,但我可能会尝试在您的 save() 调用中指定 validate:false 。 Validate() 由 save() 调用,除非您告诉 Grails 不要这样做。例如,如果您对 PGZipcode 域对象中的任何字段都有唯一约束,那么 Hibernate 必须对每条新记录进行插入以利用 DBMS 的唯一功能并执行适当的验证。其他约束可能也需要 DBMS 查询,但现在只想到独特的跳转。

来自Grails Persistence: Transaction Write-Behind

Hibernate 在可能的情况下缓存数据库更新,只是实际上 当它知道需要刷新时推送更改,或者当 刷新以编程方式触发。 Hibernate 的一种常见情况 将刷新缓存的更新是在执行查询时,因为缓存 信息可能包含在查询结果中。但只要 你正在做非冲突的保存、更新和删除,它们将是 批处理,直到会话被刷新。


或者,您可以尝试明确设置 Hibernate 会话的 flush mode

sessionFactory.currentSession.setFlushMode(FlushMode.MANUAL);

我的印象是默认的冲洗模式可能是 AUTO。

【讨论】:

  • 没有帮助。我的保存调用现在是 zipcode_pg.save(flush:false, validate: false)。
  • 无赖。通过跟踪堆栈进入 Hibernate 领域,我已经成功地找到了此类问题的根源。如果您为 Hibernate 启用调试日志记录(org.hibernate.impl.SessionImpl 可能是一个很好的入门类),您可以键入触发 SessionImpl.flush() 的路径
猜你喜欢
  • 1970-01-01
  • 2013-04-03
  • 1970-01-01
  • 1970-01-01
  • 2012-10-12
  • 2021-11-23
  • 1970-01-01
  • 2014-08-27
相关资源
最近更新 更多