Postgresql Grails保存不尊重刷新选项

Postgresql Grails保存不尊重刷新选项,postgresql,grails,gorm,Postgresql,Grails,Gorm,我将grails用作穷人的etl工具,用于将一些相对较小的db对象从1db迁移到下一个db。我有一个控制器,它从一个数据库(mysql)读取数据并将其写入另一个数据库(pgsql)。它们使用类似的域对象,但由于Grails2.1.X中对多数据源支持的限制,它们并不完全相同 下面您将看到我的控制器和服务代码: class GeoETLController { def zipcodeService def migrateZipCode() { def zc =

我将grails用作穷人的etl工具,用于将一些相对较小的db对象从1db迁移到下一个db。我有一个控制器,它从一个数据库(mysql)读取数据并将其写入另一个数据库(pgsql)。它们使用类似的域对象,但由于Grails2.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()
    }
}
类ZipcodeService{
def会话工厂
def propertyInstanceMap=org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin.PROPERTY\u实例\u映射
def readMysql(){
def zipcode_mysql=zipcode.list();
println(“读取,+zipcode_mysql.size());
返回zipcode_mysql;
}
def writePgSql(ZIPCODE){
List zips=new ArrayList();
println(“正在尝试保存,”+zipcodes.size());
def cntr=0;
zipcodes.each({Zipcode Zipcode->
cntr++;
def props=zipcode.properties;
PGZipcode zipcode_pg=新的PGZipcode(zipcode.properties);
如果(!zipcode_pg.save(刷新:false)){
zipcode_pg.errors.each{
打印它
}
}
zips.add(zipcode_pg)
如果(zips.size()%100==0){
println(“gorm begin”+新日期());
//这里没有会议。
这个.cleanUpGorm();
println(“gorm complete”+新日期());
}
});
//保存剩余的
这个.cleanUpGorm();
println(“最终版”+新日期());
}
def cleanUpGorm(){
def会话=sessionFactory.currentSession
session.flush()
会话.清除()
propertyInstanceMap.get().clear()
}
}
这其中的大部分是从我自己的代码中提取的,然后经过调整,以尝试获得类似于中所示的性能

因此,在查看我的代码时,只要调用zipcode_pg.save(),就会创建一条insert语句并发送到数据库。有利于数据库一致性,不利于批量操作

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

感谢您的建议


注意:我考虑过使用纯ETL工具,但由于已经构建了如此多的域和服务逻辑,我认为使用grails可以很好地重用资源。但是,在没有看到域对象的情况下,我无法想象批量操作的质量,这只是一种预感,但我可能会尝试在save()调用中指定validate:false。save()调用Validate(),除非您告诉Grails不要这样做。例如,如果在PGZipcode域对象中的任何字段上都有唯一约束,则Hibernate必须对每个新记录进行插入,以利用DBMS的唯一功能并执行适当的验证。其他约束也可能需要DBMS查询,但现在只想到唯一的跳跃

Hibernate只在可能的情况下缓存数据库更新 当它知道需要刷新或 刷新是通过编程方式触发的休眠的一种常见情况 将刷新缓存的更新是在执行查询时,因为缓存 信息可能包含在查询结果中。但只要 您正在执行无冲突的保存、更新和删除,它们将 批处理,直到刷新会话


或者,您可以尝试显式设置Hibernate会话的:

sessionFactory.currentSession.setFlushMode(FlushMode.MANUAL);

我觉得默认的刷新模式可能是自动的。

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

Hibernate只在可能的情况下缓存数据库更新 当它知道需要刷新或 刷新是通过编程方式触发的休眠的一种常见情况 将刷新缓存的更新是在执行查询时,因为缓存 信息可能包含在查询结果中。但只要 您正在执行无冲突的保存、更新和删除,它们将 批处理,直到刷新会话


或者,您可以尝试显式设置Hibernate会话的:

sessionFactory.currentSession.setFlushMode(FlushMode.MANUAL);

我的印象是默认的刷新模式可能是自动的。

您是否分析了代码以验证db交互是否是瓶颈?我这样问是因为你的代码看起来不错。我看到了一些与一起使用validate和flush选项有关的bug,但不只是单独使用flush。14秒的插入速度似乎非常非常慢。我在“调试模式”下运行代码,以确保每次保存时都会执行insert语句(即从db pre-save()和post-save()中运行select count(*),并看到n+1)。我还使用了计时语句,以确保速度较慢的是保存部分。我想瓶颈可能在DB端,但即使性能不佳的数据库也不应该看到这样的性能水平。您是否分析了代码以验证DB交互是瓶颈?我这样问是因为你的代码看起来不错。我看到了一些与一起使用validate和flush选项有关的bug,但不只是单独使用flush。每秒插入14次似乎非常非常慢。我在“调试模式”t下运行代码