Spring Batch - 分区超时

发布于 2025-01-11 17:16:27 字数 5008 浏览 0 评论 0原文

我必须通过 WAN 网络将数百万个 blob 记录作为文件从多个 mysql 数据库迁移到物理位置。 我选择使用 Spring Batch 并且已经让它工作了。但是,我正在努力解决随机分区步骤发生的超时错误。

这是一些背景信息,

  • 20 年内有多个 MySql 数据库存储了超过 1000 万条记录。
  • 源表索引了 varchar 数据类型中的两个复合键(没有 ID 键),因此我必须使用日期时间格式的 UN 索引列来按年和周对记录进行分区,以使每个分区的记录数合理地保持在平均200条记录。如果有更好的建议,也欢迎提出!

我的问题:当每个分区的记录足够高时,stepExecutors 将由于超时而随机失败

Could not open JDBC Con nection for transaction; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms

我已经对 DataSource 属性和 Transaction 属性进行了一些调整,但没有运气。我可以得到一些建议吗?谢谢

终端日志:

org.springframework.transaction.CannotCreateTransactionException:无法打开 JDBC Con 交易联系;嵌套异常是 java.sql.SQLTransientConnectionException:HikariPool-1 - 连接不可用,请求在 30000 毫秒后超时。
在 org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:309) 〜[spring-jdbc-5.3.16.jar:5.3.16] ... 原因:java.sql.SQLTransientConnectionException:HikariPool-1 - 连接不可用,请求在 30000 毫秒后超时。

2022-03-05 10:05:43.146 ERROR 15624 --- [main] osbatch.core.step.AbstractStep :在作业 mainJob 中执行步骤 managerStep 时遇到错误 org.springframework.batch.core.JobExecutionException:分区处理程序在...返回了一个不成功的步骤

该作业有时被标记为[FAILED]或[UNKNOWN],并且不可重新启动。

org.springframework.batch.core.partition.support.PartitionStep.doExecute(PartitionStep.java:112) ~[spring-batch-core-4.3.5.jar:4.3.5] 2022-03-05 10:05:43.213 INFO 15624 --- [main] osbclsupport.SimpleJobLauncher : 作业:[SimpleJob: [name=mainJob]] 使用以下参数完成:[{run.id=20}] 和以下状态:[FAILED] 在 3 分 13 秒 783 毫秒内 2022-03-05 10:05:43.590 INFO 15624 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource:HikariPool-2 - 关闭已启动... 2022-03-05 10:05:43.624 INFO 15624 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource:HikariPool-2 - 关闭已完成。 2022-03-05 10:05:43.626 INFO 15624 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource:HikariPool-1 - 关闭已启动... 2022-03-05 10:05:43.637 INFO 15624 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource:HikariPool-1 - 关闭已完成。

数据源生成器:我尝试增加连接超时和池大小,但似乎不适用。

@Bean(name = "srcDataSource")
// @ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariDataSource dataSource() {

    HikariDataSource hikariDS = new HikariDataSource();
    hikariDS.setDriverClassName("com.mysql.jdbc.Driver");
    hikariDS.setJdbcUrl("jdbc:mysql://dburl");
    hikariDS.setUsername("dbuser");
    hikariDS.setPassword("dbpwd");
    
    // properties below does not solve the problem
    hikariDS.setMaximumPoolSize(16);
    hikariDS.setConnectionTimeout(30000); 

    // hikariDS.addDataSourceProperty("serverName",
    // getConfig().getString("mysql.host"));
    // hikariDS.addDataSourceProperty("port", getConfig().getString("mysql.port"));
    // hikariDS.addDataSourceProperty("databaseName",
    // getConfig().getString("mysql.database"));
    // hikariDS.addDataSourceProperty("user", getConfig().getString("mysql.user"));
    // hikariDS.addDataSourceProperty("password",
    // getConfig().getString("mysql.password"));
    // hikariDS.addDataSourceProperty("autoReconnect", true);
    // hikariDS.addDataSourceProperty("cachePrepStmts", true);
    // hikariDS.addDataSourceProperty("prepStmtCacheSize", 250);
    // hikariDS.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
    // hikariDS.addDataSourceProperty("useServerPrepStmts", true);
    // hikariDS.addDataSourceProperty("cacheResultSetMetadata", true);

    return hikariDS;
}

ManagerStep:

@Bean
public Step managerStep() {
    return stepBuilderFactory.get("managerStep")
        .partitioner(workerStep().getName(), dateRangePartitioner())
        .step(workerStep())
        // .gridSize(52) // number of worker, which is not necessary with datepartition
        .taskExecutor(new SimpleAsyncTaskExecutor())
        .build();
}

WorkerStep:我也尝试增加事务属性超时,但运气不佳

@Bean
public Step workerStep() {
    DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
    attribute.setPropagationBehavior(Propagation.REQUIRED.value());
    attribute.setIsolationLevel(Isolation.DEFAULT.value());
    // attribute.setTimeout(30);
    attribute.setTimeout(1000000);

    return stepBuilderFactory.get("workerStep")
            .<Image, Image>chunk(10)
            .reader(jdbcPagingReader(null))
            .processor(new ImageItemProcessor())            
            .writer(imageConverter())
            // .listener(wrkrStepExecutionListener)
            .transactionAttribute(attribute)
            .build();
}

作业生成器:

@Bean
public Job mainJob() {
    return jobBuilderFactory.get("mainJob")
        // .incrementer(new RunIdIncrementer())
        .start(managerStep())
        // .listener()
        .build();
}

I have to migrate around millions of blob records from multiple mysql databases to a physical location as files over WAN network.
I chose to use Spring Batch and has already made it work. However, I am struggling with a timeout error happen with random partitioned steps.

Here is some context,

  • There are multiple MySql database store >10m records in 20 years.
  • The source tables indexed two composite keys in varchar datatype (there is no ID key) so I have to use an UN-indexed column in date-time format to partitioning the records by year and week to keep the number of records per partition reasonably at average 200 records. If there is any better advice, it would be welcome!

My issue: When the records per partition is high enough, the stepExecutors will randomly failed due to time out

Could not open JDBC Con nection for transaction; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms

I have done some tweaks with the DataSource properties and Transaction properties but no luck. Can I get some advice please! Thanks

Terminal log:

org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Con
nection for transaction; nested exception is
java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.
at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:309)
~[spring-jdbc-5.3.16.jar:5.3.16]
...
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.

2022-03-05 10:05:43.146 ERROR 15624 --- [main] o.s.batch.core.step.AbstractStep : Encountered an error executing step managerStep in job mainJob
org.springframework.batch.core.JobExecutionException: Partition handler returned an unsuccessful step at ...

The job is marked as [FAILED] or [UNKNOWN] sometimes, and not restartable.

org.springframework.batch.core.partition.support.PartitionStep.doExecute(PartitionStep.java:112) ~[spring-batch-core-4.3.5.jar:4.3.5]
2022-03-05 10:05:43.213 INFO 15624 --- [main] o.s.b.c.l.support.SimpleJobLauncher : Job: [SimpleJob: [name=mainJob]] completed with the following parameters: [{run.id=20}] and the following status: [FAILED] in 3m13s783ms
2022-03-05 10:05:43.590 INFO 15624 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Shutdown initiated...
2022-03-05 10:05:43.624 INFO 15624 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Shutdown completed.
2022-03-05 10:05:43.626 INFO 15624 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2022-03-05 10:05:43.637 INFO 15624 --- [SpringApplicationShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.

Datasource builder: I have tried to increase the connection timeout and pool size, but it seems not be applied.

@Bean(name = "srcDataSource")
// @ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariDataSource dataSource() {

    HikariDataSource hikariDS = new HikariDataSource();
    hikariDS.setDriverClassName("com.mysql.jdbc.Driver");
    hikariDS.setJdbcUrl("jdbc:mysql://dburl");
    hikariDS.setUsername("dbuser");
    hikariDS.setPassword("dbpwd");
    
    // properties below does not solve the problem
    hikariDS.setMaximumPoolSize(16);
    hikariDS.setConnectionTimeout(30000); 

    // hikariDS.addDataSourceProperty("serverName",
    // getConfig().getString("mysql.host"));
    // hikariDS.addDataSourceProperty("port", getConfig().getString("mysql.port"));
    // hikariDS.addDataSourceProperty("databaseName",
    // getConfig().getString("mysql.database"));
    // hikariDS.addDataSourceProperty("user", getConfig().getString("mysql.user"));
    // hikariDS.addDataSourceProperty("password",
    // getConfig().getString("mysql.password"));
    // hikariDS.addDataSourceProperty("autoReconnect", true);
    // hikariDS.addDataSourceProperty("cachePrepStmts", true);
    // hikariDS.addDataSourceProperty("prepStmtCacheSize", 250);
    // hikariDS.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
    // hikariDS.addDataSourceProperty("useServerPrepStmts", true);
    // hikariDS.addDataSourceProperty("cacheResultSetMetadata", true);

    return hikariDS;
}

ManagerStep:

@Bean
public Step managerStep() {
    return stepBuilderFactory.get("managerStep")
        .partitioner(workerStep().getName(), dateRangePartitioner())
        .step(workerStep())
        // .gridSize(52) // number of worker, which is not necessary with datepartition
        .taskExecutor(new SimpleAsyncTaskExecutor())
        .build();
}

WorkerStep: I also tried to increase the transaction properties timeout, but not luck

@Bean
public Step workerStep() {
    DefaultTransactionAttribute attribute = new DefaultTransactionAttribute();
    attribute.setPropagationBehavior(Propagation.REQUIRED.value());
    attribute.setIsolationLevel(Isolation.DEFAULT.value());
    // attribute.setTimeout(30);
    attribute.setTimeout(1000000);

    return stepBuilderFactory.get("workerStep")
            .<Image, Image>chunk(10)
            .reader(jdbcPagingReader(null))
            .processor(new ImageItemProcessor())            
            .writer(imageConverter())
            // .listener(wrkrStepExecutionListener)
            .transactionAttribute(attribute)
            .build();
}

Job builder:

@Bean
public Job mainJob() {
    return jobBuilderFactory.get("mainJob")
        // .incrementer(new RunIdIncrementer())
        .start(managerStep())
        // .listener()
        .build();
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文