使用Spring批处理,EhCache和Hibernate逐步降低数据导入性能

时间:2017-12-08 09:00:36

标签: spring hibernate spring-batch ehcache

我正在使用Spring MVC 4,Spring Batch 3,Hibernate 5和Ehcache 2.8。使用Spring Batch,我使用Hibernate以1000块的形式将大量数据从CSV导入数据库。有关其他信息,CSV中的数据指的是其他表,这就是我在实际插入之前有大量数据库查询的原因。

首先,块之间的间隔很小,在10秒之间。逐渐地,在大约100,000件物品之后,物品之间的间隔超过1分钟。我怀疑这是一些缓存问题,因为这是逐渐退化。我目前的hibernate统计信息是

  • 二级缓存命中率:97.8839177750907
  • 查询缓存命中率:54.206282344445775
  • 查询最长时间:导入中使用的查询为4.08秒

这是我的ehcache.xml配置

<defaultCache 
    eternal="false"
    timeToIdleSeconds="180" 
    timeToLiveSeconds="240" 
    maxEntriesLocalHeap="10000"
    maxEntriesLocalDisk="50000" 
    >
    <persistence strategy="localTempSwap" />
</defaultCache>

<cache name="org.hibernate.cache.internal.StandardQueryCache" 
    maxEntriesLocalHeap="10000"
    maxEntriesLocalDisk="50000"
    eternal="false"
    timeToIdleSeconds="180"
    timeToLiveSeconds="240"
    >
    <persistence strategy="localTempSwap" />
</cache>

<cache name="org.hibernate.cache.spi.UpdateTimestampsCache" 
    eternal="false" 
    maxEntriesLocalHeap="0"
/>

[编辑],这是批处理作业的源代码

@Bean
@StepScope
public static FlatFileItemReader<CsvPayment> paymentReader( @Value( "#{jobParameters[fullPathFileName]}" ) String pathToFile,
                                                            @Value( "#{jobParameters[delimeter]}" ) String delimeter,
                                                            @Value( "#{jobParameters[skipItems]}" ) Long skipItems,
                                                            @Value( "#{jobParameters[limitItems]}" ) Long limitItems )
{
    FlatFileItemReader<CsvPayment> reader = new FlatFileItemReader<>();

    reader.setResource( new FileSystemResource( pathToFile ) );
    reader.setEncoding( GlobalConstants.UTF8 );
    reader.setMaxItemCount( limitItems.intValue() );
    reader.setLinesToSkip( skipItems.intValue() );
    reader.setLineMapper( new CsvPaymentLineMapper( delimeter ) );

    return reader;
}

@Bean( "importPayment" )
public Job importPayment( ItemReader<CsvPayment> paymentReader )
{
    return jobBuilderFactory.get( "paymentReader" ).incrementer( new RunIdIncrementer() ).flow(
        paymentStep1( paymentReader ) ).end().build();
}

@Bean
public Step paymentStep1( ItemReader<CsvPayment> paymentReader )
{
    return stepBuilderFactory.get( "paymentStep1" ).<CsvPayment, OccupancyPayment> chunk( CHUNK_SIZE ).reader(
        paymentReader ).processor( itemProcessor ).faultTolerant().listener(
            new ChunkListenerImpl( logger ) ).writer( itemWriter() ).build();
}

@Bean
public HibernateItemWriter<OccupancyPayment> itemWriter()
{
    HibernateItemWriter<OccupancyPayment> itemWriter = new HibernateItemWriter<>();
    itemWriter.setSessionFactory( sessionFactory );
    itemWriter.setClearSession( true );
    return itemWriter;
}

希望有人可以指出我正确的方向。感谢。

1 个答案:

答案 0 :(得分:0)

会话缓存正在增长,使您的应用程序变慢并消耗更多内存。

试试这个:

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

for ( int i=0; i<100000; i++ ) {
    Customer customer = new Customer(.....);
    session.save(customer);
    if ( i % 20 == 0 ) { //20, same as the JDBC batch size
        //flush a batch of inserts and release memory:
        session.flush();
        session.clear();
    }
}

tx.commit();
session.close();

取自here的示例,我向您确认我们使用相同的解决方案并且效果很好。

解决方案的唯一缺点是有时你可能会从Hibernate获得NonUniqueObjectException。它是session.clear()的结果,它使所有Hibernate实例分离。在这种情况下,您肯定需要阅读this并且可能会调用session.refresh(instance)