Spring Batch慢速读写

时间:2019-03-11 14:37:43

标签: spring spring-boot spring-batch

我有一个批处理作业,可以从SQLServer读取记录并写入MariaDB。即使我在批处理过程中实现了分区的概念,该过程也很慢

下面是源系统和目标系统的数据源配置。

@Bean(name = "sourceSqlServerDataSource")
    public DataSource mysqlDataSource() {
        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setMaximumPoolSize(100);
        hikariDataSource.setUsername(username);
        hikariDataSource.setPassword(password);
        hikariDataSource.setJdbcUrl(jdbcUrl);
        hikariDataSource.setDriverClassName(driverClassName);
        hikariDataSource.setPoolName("Source-SQL-Server");
        return hikariDataSource;
    } 

    @Bean(name = "targetMySqlDataSource")
    @Primary
    public DataSource mysqlDataSource() {
        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setMaximumPoolSize(100);
        hikariDataSource.setUsername(username);
        hikariDataSource.setPassword(password);
        hikariDataSource.setJdbcUrl(jdbcUrl);
        hikariDataSource.setDriverClassName(driverClassName);
        hikariDataSource.setPoolName("Target-Myql-Server");
        return hikariDataSource;
    }

下面是配置的My Bean和线程池taskexecutor

@Bean(name = "myBatchJobsThreadPollTaskExecutor")
    public ThreadPoolTaskExecutor initializeThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(100);
        threadPoolTaskExecutor.setMaxPoolSize(200);
        threadPoolTaskExecutor.setThreadNamePrefix("My-Batch-Jobs-TaskExecutor ");
        threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(Boolean.TRUE);
        threadPoolTaskExecutor.initialize();
        log.info("Thread Pool Initialized with min {} and Max {} Pool Size",threadPoolTaskExecutor.getCorePoolSize(),threadPoolTaskExecutor.getMaxPoolSize() );
        return threadPoolTaskExecutor;
    }

这是配置的步骤和分区步骤

@Bean(name = "myMainStep")
    public Step myMainStep() throws Exception{
        return stepBuilderFactory.get("myMainStep").chunk(500)
                .reader(myJdbcReader(null,null))
                .writer(myJpaWriter()).listener(chunkListener)
                .build();
    }

    @Bean
    public Step myPartitionStep() throws Exception {
        return stepBuilderFactory.get("myPartitionStep").listener(myStepListener)
                .partitioner(myMainStep()).partitioner("myPartition",myPartition)
                .gridSize(50).taskExecutor(asyncTaskExecutor).build();
    }

与读者和作家一起更新帖子

@Bean(name = "myJdbcReader")
    @StepScope
    public JdbcPagingItemReader myJdbcReader(@Value("#{stepExecutionContext[parameter1]}") Integer parameter1, @Value("#{stepExecutionContext[parameter2]}") Integer parameter2) throws Exception{
        JdbcPagingItemReader jdbcPagingItemReader = new JdbcPagingItemReader();
        jdbcPagingItemReader.setDataSource(myTargetDataSource);
        jdbcPagingItemReader.setPageSize(500);
        jdbcPagingItemReader.setRowMapper(myRowMapper());
        Map<String,Object> paramaterMap=new HashMap<>();
        paramaterMap.put("parameter1",parameter1);
        paramaterMap.put("parameter2",parameter2);
        jdbcPagingItemReader.setQueryProvider(myQueryProvider());
        jdbcPagingItemReader.setParameterValues(paramaterMap);
        return jdbcPagingItemReader;
    }

    @Bean(name = "myJpaWriter")
    public ItemWriter myJpaWriter(){
        JpaItemWriter<MyTargetTable> targetJpaWriter = new JpaItemWriter<>();
        targetJpaWriter.setEntityManagerFactory(localContainerEntityManagerFactoryBean.getObject());
        return targetJpaWriter;
    }

有人可以谈谈如何使用Spring batch提高读写性能吗??

1 个答案:

答案 0 :(得分:0)

提高此类应用程序的性能取决于多个参数(网格大小,块大小,页面大小,线程池大小,数据库连接池大小,数据库服务器与JVM之间的延迟等)。因此,我无法为您提供精确的答案,但我会尝试提供一些指导原则:

  • 在开始提高性能之前,您需要明确定义基线+目标。说“它很慢”没有任何意义。至少使用JVM Profiler和带有查询执行计划分析器的SQL客户端来做好准备。在JVM或数据库上找到性能瓶颈是必需的。
  • 将网格大小设置为50,并使用核心大小= 100的线程池意味着将创建50个线程,但不使用它们。确保您在.taskExecutor(asyncTaskExecutor)中使用线程池任务执行器,而不是在不重用线程的SimpleAsyncTaskExecutor中使用线程。
  • 对我而言,
  • 用于250k记录的50个分区似乎很多。每个分区将有5000条记录,每个分区将产生10个事务(因为chunkSize = 500)。因此,两个数据库服务器和JVM之间将有10个事务x 50个分区= 500个事务。这可能是性能问题。我建议从较少的分区开始,例如5或10。 增加并发并不一定意味着提高性能。总会有一个收支平衡点,您的应用程序将花费更多的时间进行上下文切换和并发处理,而不是执行业务逻辑。找到这一点是一个经验过程。
  • 我将首先运行任何Spring Batch作业的任何sql查询 outside ,以查看查询本身(查询抓取过多的列,太多的记录等)还是与之相关的性能问题数据库模式(例如缺少索引)
  • 我不会将JPA / Hibernate用于此类ETL作业。将数据映射到域对象可能会很昂贵,特别是如果O / R映射未优化的话。在这些情况下,原始JDBC通常会更快。

还有很多其他技巧,例如估算内存中的项目大小,并确保内存中的总块大小为<堆大小,以避免块中不必要的GC,为批处理应用程序选择正确的GC算法,等等。有点先进。上面的指南列表是IMO的一个很好的起点。

希望这会有所帮助!