【问题标题】:Spring Batch Slow Write and ReadSpring Batch 慢写和读
【发布时间】:2019-08-01 21:51:12
【问题描述】:

我有一个批处理作业,从 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 批处理提高读写性能...?

【问题讨论】:

  • 你也可以发布读者/作者吗?
  • @StanislavL 感谢您的回复。我已经用读者和作者更新了这篇文章
  • 你有没有分析过?瓶颈是什么?
  • @MichaelMinella 对不起,您能否指导/建议如何实现分析并找出瓶颈。由于我已经实现了步骤执行侦听器,工作开始和结束之间的时间差是25 万条记录需要 30 分钟
  • 1.添加必要的配置以在生成的日志中输出 sql 以提取数据。 2.使用sql客户端手动运行sql,查看查询执行计划。查找表扫描。如果您看到表扫描可能会创建一个索引。 3. 还要确认应用服务器和数据库服务器之间的延迟。

标签: spring spring-boot spring-batch


【解决方案1】:

提高此类应用程序的性能取决于多个参数(网格大小、块大小、页面大小、线程池大小、数据库连接池大小、数据库服务器和 JVM 之间的延迟等)。所以我不能给你一个准确的答案,但我会尽力提供一些指导方针:

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

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

希望这会有所帮助!

【讨论】:

  • 非常感谢您的见解。当我删除并再次创建目标表时,问题得到了解决
猜你喜欢
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-20
  • 2018-02-06
  • 2013-09-30
  • 2022-11-11
  • 1970-01-01
相关资源
最近更新 更多