精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

自從用了 Spring Batch,效率飆升500%!

開發(fā) 架構(gòu)
場景1:銀行每日利息計算。痛點:?凌晨時段需掃描百萬級賬戶數(shù)據(jù),手工計算容易遺漏。Spring Batch方案:?分片讀取賬戶數(shù)據(jù),批量計算利息,失敗自動重試。實際案例:?某銀行系統(tǒng)改造后,利息計算時間從4小時縮短至23分鐘。

一、為什么需要批處理?

1. 應(yīng)用場景解析

場景1:銀行每日利息計算

圖片圖片

  • 痛點: 凌晨時段需掃描百萬級賬戶數(shù)據(jù),手工計算容易遺漏
  • Spring Batch方案: 分片讀取賬戶數(shù)據(jù),批量計算利息,失敗自動重試
  • 實際案例: 某銀行系統(tǒng)改造后,利息計算時間從4小時縮短至23分鐘
場景2:電商訂單歸檔
// 傳統(tǒng)SQL示例(存在性能問題)
DELETE FROM active_orders 
WHERE create_time < '2023-01-01'
LIMIT 5000; // 需循環(huán)執(zhí)行直到無數(shù)據(jù)
  • 問題: 直接刪除百萬級數(shù)據(jù)會導(dǎo)致數(shù)據(jù)庫鎖表
  • 正確做法: 使用Spring Batch分頁讀取→寫入歷史表→批量刪除
場景3:日志分析

圖片圖片

  • 典型需求: 分析Nginx日志中的API響應(yīng)時間分布
  • 特殊挑戰(zhàn): 處理GB級文本文件時的內(nèi)存控制
場景4:醫(yī)療數(shù)據(jù)遷移

圖片圖片

  • 特殊要求: 遷移過程中老系統(tǒng)仍需正常使用
  • 解決方案: 使用Spring Batch的增量遷移模式

2. 傳統(tǒng)方式痛點

圖片圖片

詳細解釋每個痛點:

  • 資源管理復(fù)雜
// 典型的多線程錯誤示例
ExecutorService executor = Executors.newFixedThreadPool(8);
try {
    while(hasNextPage()) {
        List<Data> page = fetchNextPage();
        executor.submit(() -> processPage(page)); // 可能引發(fā)內(nèi)存泄漏
    }
} finally {
    executor.shutdown(); // 忘記調(diào)用會導(dǎo)致線程堆積
}

常見問題:線程池配置不當(dāng)導(dǎo)致OOM、數(shù)據(jù)庫連接泄露

  • 容錯性黑洞
// 偽代碼:脆弱的錯誤處理
for (int i=0; i<3; i++) {
    try {
        processBatch();
        break;
    } catch (Exception e) {
        if (i == 2) sendAlert(); // 簡單重試無法處理部分成功場景
    }
}

真實案例:某支付系統(tǒng)因未處理部分失敗,導(dǎo)致重復(fù)出款

  • 維護噩夢
# 典型硬編碼配置
batch.size=1000
input.path=/data/in
output.path=/data/out

問題根源:參數(shù)修改需要重新部署、不同環(huán)境配置混雜

  • 監(jiān)控盲區(qū)
# 開發(fā)人員常用的臨時方案
nohup java -jar batch.jar > log.txt 2>&1 &
tail -f log.txt # 無法獲知實時進度

關(guān)鍵缺陷:無法回答"處理到哪了?"、"還剩多少?"等業(yè)務(wù)問題

Spring Batch對比優(yōu)勢表

圖片圖片

二、Spring Batch核心架構(gòu)

1. 四大金剛組件深度解析

組件1:Job(作業(yè)工廠)

圖片圖片

  • 核心作用: 定義完整的批處理流水線(如月度報表生成流程)
  • 真實案例: 某銀行的日終對賬Job包含三個Step
@Bean
public Job reconciliationJob(){
    return jobBuilderFactory.get("dailyReconciliation")
            .start(downloadBankFileStep())
            .next(validateDataStep())
            .next(generateReportStep())
            .build();
}
組件2:Step(裝配流水線)

圖片圖片

設(shè)計模式:采用分塊(Chunk)處理機制

配置示例:

@Bean
public Step importStep(){
    return stepBuilderFactory.get("csvImport")
            .<User, User>chunk(500)  // 每500條提交一次
            .reader(csvReader())
            .processor(validationProcessor())
            .writer(dbWriter())
            .faultTolerant()
            .skipLimit(10)
            .skip(DataIntegrityViolationException.class)
            .build();
}
組件3:ItemReader(數(shù)據(jù)搬運工)

圖片圖片

  • 典型實現(xiàn):
// 讀取CSV文件示例
@Bean
public FlatFileItemReader<User> csvReader(){
    returnnew FlatFileItemReaderBuilder<User>()
            .name("userReader")
            .resource(new FileSystemResource("data/users.csv"))
            .delimited().delimiter(",")
            .names("id", "name", "email")
            .fieldSetMapper(new BeanWrapperFieldSetMapper<User>() {{
                setTargetType(User.class);
            }})
            .linesToSkip(1) // 跳過標題行
            .build();
}
組件4:ItemWriter(數(shù)據(jù)收納師)

圖片圖片

  • 復(fù)合寫入示例:
@Bean
public CompositeItemWriter<User> compositeWriter(){
    returnnew CompositeItemWriterBuilder<User>()
            .delegates(dbWriter(), logWriter(), mqWriter())
            .build();
}

// 數(shù)據(jù)庫寫入組件
private JdbcBatchItemWriter<User> dbWriter(){
    returnnew JdbcBatchItemWriterBuilder<User>()
            .dataSource(dataSource)
            .sql("INSERT INTO users (name,email) VALUES (:name,:email)")
            .beanMapped()
            .build();
}

2. 架構(gòu)示意圖

圖片圖片

3. 隱藏BOSS:ItemProcessor(數(shù)據(jù)變形金剛)

圖片圖片

  • 典型應(yīng)用:數(shù)據(jù)脫敏處理
publicclassDataMaskProcessorimplementsItemProcessor<User, User> {
    @Override
    public User process(User user){
        // 手機號脫敏
        String phone = user.getPhone();
        user.setPhone(phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
        
        // 郵箱轉(zhuǎn)小寫
        user.setEmail(user.getEmail().toLowerCase());
        
        return user;
    }
}

4. 組件生命周期探秘

圖片圖片

三、手把手開發(fā)指南

1. 環(huán)境搭建

<!-- 完整POM配置 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.1.5</version>
</parent>

<dependencies>
    <!-- Batch核心依賴 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-batch</artifactId>
    </dependency>
    
    <!-- 內(nèi)存數(shù)據(jù)庫(生產(chǎn)環(huán)境可更換為MySQL等) -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <!-- Lombok簡化代碼 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
# application.properties
spring.batch.jdbc.initialize-schema=always # 自動創(chuàng)建Batch元數(shù)據(jù)表
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver

2. 第一個批處理任務(wù)

  • 領(lǐng)域模型類:
@Data// Lombok注解
@NoArgsConstructor
@AllArgsConstructor
publicclassUser{
    private String name;
    privateint age;
    private String email;
}
  • 完整Job配置:
@Configuration
@EnableBatchProcessing
publicclassBatchConfig{

    @Autowiredprivate JobBuilderFactory jobBuilderFactory;
    @Autowiredprivate StepBuilderFactory stepBuilderFactory;

    // 定義Job
    @Bean
    public Job importUserJob(){
        return jobBuilderFactory.get("importUserJob")
                .start(csvProcessingStep())
                .build();
    }

    // 定義Step
    @Bean
    public Step csvProcessingStep(){
        return stepBuilderFactory.get("csvProcessing")
                .<User, User>chunk(100) // 每處理100條提交一次
                .reader(userReader())
                .processor(userProcessor())
                .writer(userWriter())
                .build();
    }

    // CSV文件讀取器
    @Bean
    public FlatFileItemReader<User> userReader(){
        returnnew FlatFileItemReaderBuilder<User>()
                .name("userReader")
                .resource(new ClassPathResource("users.csv")) // 文件路徑
                .delimited()
                .delimiter(",")
                .names("name", "age", "email") // 字段映射
                .targetType(User.class)
                .linesToSkip(1) // 跳過標題行
                .build();
    }

    // 數(shù)據(jù)處理(示例:年齡校驗)
    @Bean
    public ItemProcessor<User, User> userProcessor(){
        return user -> {
            if (user.getAge() < 0) {
                thrownew IllegalArgumentException("年齡不能為負數(shù): " + user);
            }
            return user.toBuilder() // 使用Builder模式創(chuàng)建新對象
                    .email(user.getEmail().toLowerCase())
                    .build();
        };
    }

    // 數(shù)據(jù)庫寫入器
    @Bean
    public JdbcBatchItemWriter<User> userWriter(DataSource dataSource){
        returnnew JdbcBatchItemWriterBuilder<User>()
                .dataSource(dataSource)
                .sql("INSERT INTO users (name, age, email) VALUES (:name, :age, :email)")
                .beanMapped()
                .build();
    }
}
  • CSV文件示例(src/main/resources/users.csv):
name,age,email
張三,25,zhangsan@example.com
李四,30,lisi@example.com
王五,-5,wangwu@example.com
  • 啟動類:
@SpringBootApplication
publicclassBatchApplicationimplementsCommandLineRunner{

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private Job importUserJob;

    publicstaticvoidmain(String[] args){
        SpringApplication.run(BatchApplication.class, args);
    }

    @Override
    publicvoidrun(String... args)throws Exception {
        JobParameters params = new JobParametersBuilder()
                .addLong("startAt", System.currentTimeMillis())
                .toJobParameters();
        jobLauncher.run(importUserJob, params);
    }
}

3. 執(zhí)行流程可視化

圖片圖片

4. 運行效果驗證

  • 控制臺輸出:
2023-10-01 10:00:00 INFO  o.s.b.c.l.support.SimpleJobLauncher - Job: [SimpleJob: [name=importUserJob]] launched
2023-10-01 10:00:05 INFO  o.s.batch.core.job.SimpleStepHandler - Executing step: [csvProcessing]
2023-10-01 10:00:15 ERROR o.s.batch.core.step.AbstractStep - Encountered an error executing step csvProcessing
org.springframework.batch.item.validator.ValidationException: 年齡不能為負數(shù): User(name=王五, age=-5, email=wangwu@example.com)
  • 數(shù)據(jù)庫結(jié)果:
SELECT * FROMusers;

圖片圖片

5. 調(diào)試技巧

  • 查看元數(shù)據(jù):
SELECT * FROM BATCH_JOB_INSTANCE;
SELECT * FROM BATCH_STEP_EXECUTION;
  • 重試失敗任務(wù):
// 在Job配置中添加容錯機制
@Bean
public Step csvProcessingStep(){
    return stepBuilderFactory.get("csvProcessing")
            .<User, User>chunk(100)
            .reader(userReader())
            .processor(userProcessor())
            .writer(userWriter())
            .faultTolerant()
            .skipLimit(3) // 最多跳過3條錯誤
            .skip(IllegalArgumentException.class)
            .build();
}
  • 日志監(jiān)控配置:
logging.level.org.springframework.batch=DEBUG
logging.level.org.hibernate.SQL=WARN

四、實戰(zhàn)案例:銀行交易對賬

1. 場景需求增強說明

核心流程:

圖片圖片

技術(shù)挑戰(zhàn):

  • 雙數(shù)據(jù)源讀取(文件+數(shù)據(jù)庫)
  • 千萬級數(shù)據(jù)高效比對
  • 差異記錄快速入庫
  • 分布式環(huán)境運行

2. 完整架構(gòu)設(shè)計

圖片圖片

3. 領(lǐng)域模型定義

@Data
@AllArgsConstructor
@NoArgsConstructor
publicclassTransaction{
    // 公共字段
    private String transactionId;
    private LocalDateTime tradeTime;
    private BigDecimal amount;
    
    // 銀行端數(shù)據(jù)
    private String bankSerialNo;
    private BigDecimal bankAmount;
    
    // 內(nèi)部系統(tǒng)數(shù)據(jù)
    private String internalOrderNo;
    private BigDecimal systemAmount;
    
    // 對賬結(jié)果
    private ReconStatus status;
    private String discrepancyType;
}

publicenum ReconStatus {
    MATCHED,       // 數(shù)據(jù)一致
    AMOUNT_DIFF,   // 金額不一致
    STATUS_DIFF,    // 狀態(tài)不一致
    ONLY_IN_BANK,   // 銀行單邊賬
    ONLY_IN_SYSTEM  // 系統(tǒng)單邊賬
}

4. 完整Job配置

@Configuration
@EnableBatchProcessing
publicclassBankReconJobConfig{

    // 主Job定義
    @Bean
    public Job bankReconciliationJob(Step downloadStep, Step reconStep, Step reportStep){
        return jobBuilderFactory.get("bankReconciliationJob")
                .start(downloadStep)
                .next(reconStep)
                .next(reportStep)
                .build();
    }

    // 文件下載Step
    @Bean
    public Step downloadStep(){
        return stepBuilderFactory.get("downloadStep")
                .tasklet((contribution, chunkContext) -> {
                    // 實現(xiàn)SFTP下載邏輯
                    sftpService.download("/bank/recon/20231001.csv");
                    return RepeatStatus.FINISHED;
                })
                .build();
    }

    // 核心對賬Step
    @Bean
    public Step reconStep(){
        return stepBuilderFactory.get("reconStep")
                .<Transaction, Transaction>chunk(1000)
                .reader(compositeReader())
                .processor(compositeProcessor())
                .writer(compositeWriter())
                .faultTolerant()
                .skipLimit(100)
                .skip(DataIntegrityViolationException.class)
                .retryLimit(3)
                .retry(DeadlockLoserDataAccessException.class)
                .build();
    }

    // 組合數(shù)據(jù)讀取器
    @Bean
    public CompositeItemReader<Transaction> compositeReader(){
        returnnew CompositeItemReaderBuilder<Transaction>()
                .delegates(bankFileReader(), internalDbReader())
                .build();
    }

    // 銀行文件讀取器
    @Bean
    public FlatFileItemReader<Transaction> bankFileReader(){
        returnnew FlatFileItemReaderBuilder<Transaction>()
                .name("bankFileReader")
                .resource(new FileSystemResource("recon/20231001.csv"))
                .delimited()
                .names("transactionId","tradeTime","amount","bankSerialNo")
                .fieldSetMapper(fieldSet -> {
                    Transaction t = new Transaction();
                    t.setTransactionId(fieldSet.readString("transactionId"));
                    t.setBankSerialNo(fieldSet.readString("bankSerialNo"));
                    t.setBankAmount(fieldSet.readBigDecimal("amount"));
                    return t;
                })
                .build();
    }

    // 內(nèi)部數(shù)據(jù)庫讀取器
    @Bean
    public JdbcCursorItemReader<Transaction> internalDbReader(){
        returnnew JdbcCursorItemReaderBuilder<Transaction>()
                .name("internalDbReader")
                .dataSource(internalDataSource)
                .sql("SELECT order_no, amount, status FROM transactions WHERE trade_date = ?")
                .rowMapper((rs, rowNum) -> {
                    Transaction t = new Transaction();
                    t.setInternalOrderNo(rs.getString("order_no"));
                    t.setSystemAmount(rs.getBigDecimal("amount"));
                    return t;
                })
                .preparedStatementSetter(ps -> ps.setString(1, "2023-10-01"))
                .build();
    }

    // 組合處理器
    @Bean
    public CompositeItemProcessor<Transaction> compositeProcessor(){
        List<ItemProcessor<?, ?>> delegates = new ArrayList<>();
        delegates.add(new DataMatchingProcessor());
        delegates.add(new DiscrepancyClassifier());
        returnnew CompositeItemProcessorBuilder<>()
                .delegates(delegates)
                .build();
    }

    // 組合寫入器
    @Bean
    public CompositeItemWriter<Transaction> compositeWriter(){
        returnnew CompositeItemWriterBuilder<Transaction>()
                .delegates(
                    discrepancyDbWriter(),
                    alertMessageWriter()
                )
                .build();
    }
}

5. 核心處理器實現(xiàn)

publicclassDataMatchingProcessorimplementsItemProcessor<Transaction, Transaction> {

    @Override
    public Transaction process(Transaction item){
        // 雙數(shù)據(jù)源匹配邏輯
        if (item.getBankSerialNo() == null) {
            item.setStatus(ReconStatus.ONLY_IN_SYSTEM);
        } elseif (item.getInternalOrderNo() == null) {
            item.setStatus(ReconStatus.ONLY_IN_BANK);
        } else {
            compareAmounts(item);
            compareStatuses(item);
        }
        return item;
    }

    privatevoidcompareAmounts(Transaction t){
        if (t.getBankAmount().compareTo(t.getSystemAmount()) != 0) {
            t.setDiscrepancyType("AMOUNT_MISMATCH");
            t.setStatus(ReconStatus.AMOUNT_DIFF);
            BigDecimal diff = t.getBankAmount().subtract(t.getSystemAmount());
            t.setAmount(diff.abs());
        }
    }

    privatevoidcompareStatuses(Transaction t){
        // 假設(shè)從數(shù)據(jù)庫獲取內(nèi)部狀態(tài)
        String internalStatus = transactionService.getStatus(t.getInternalOrderNo());
        if(!"SETTLED".equals(internalStatus)){
            t.setDiscrepancyType("STATUS_MISMATCH");
            t.setStatus(ReconStatus.STATUS_DIFF);
        }
    }
}

publicclassDiscrepancyClassifierimplementsItemProcessor<Transaction, Transaction> {
    @Override
    public Transaction process(Transaction item){
        if (item.getStatus() != ReconStatus.MATCHED) {
            // 添加告警標記
            item.setAlertLevel(calculateAlertLevel(item));
        }
        return item;
    }

    private AlertLevel calculateAlertLevel(Transaction t){
        if (t.getAmount().compareTo(new BigDecimal("1000000")) > 0) {
            return AlertLevel.CRITICAL;
        }
        return AlertLevel.WARNING;
    }
}

6. 差異報告生成Step

@Bean
public Step reportStep(){
    return stepBuilderFactory.get("reportStep")
            .<Transaction, Transaction>chunk(1000)
            .reader(discrepancyReader())
            .writer(excelWriter())
            .build();
}

@Bean
public JdbcPagingItemReader<Transaction> discrepancyReader(){
    returnnew JdbcPagingItemReaderBuilder<Transaction>()
            .name("discrepancyReader")
            .dataSource(reconDataSource)
            .selectClause("SELECT *")
            .fromClause("FROM discrepancy_records")
            .whereClause("WHERE recon_date = '2023-10-01'")
            .sortKeys(Collections.singletonMap("transaction_id", Order.ASCENDING))
            .rowMapper(new BeanPropertyRowMapper<>(Transaction.class))
            .build();
}

@Bean
public ExcelFileItemWriter<Transaction> excelWriter(){
    returnnew ExcelFileItemWriterBuilder<Transaction>()
            .name("excelWriter")
            .resource(new FileSystemResource("reports/2023-10-01.xlsx"))
            .sheetName("差異報告")
            .headers(new String[]{"交易ID", "差異類型", "金額差異", "告警級別"})
            .fieldExtractor(item -> new Object[]{
                    item.getTransactionId(),
                    item.getDiscrepancyType(),
                    item.getAmount(),
                    item.getAlertLevel()
            })
            .build();
}

7. 性能優(yōu)化配置

# 應(yīng)用配置
spring.batch.job.enabled=false# 禁止自動啟動
spring.batch.initialize-schema=never # 生產(chǎn)環(huán)境禁止自動建表

# 性能調(diào)優(yōu)參數(shù)
spring.batch.chunk.size=2000 # 根據(jù)內(nèi)存調(diào)整
spring.datasource.hikari.maximum-pool-size=20
spring.jpa.properties.hibernate.jdbc.batch_size=1000

8. 執(zhí)行監(jiān)控看板

圖片圖片

五、生產(chǎn)級特性

1. 容錯機制

圖片圖片

  • 完整容錯配置示例:
@Bean
public Step secureStep(){
    return stepBuilderFactory.get("secureStep")
            .<Input, Output>chunk(500)
            .reader(jdbcReader())
            .processor(secureProcessor())
            .writer(restApiWriter())
            .faultTolerant()
            .retryLimit(3)
            .retry(ConnectException.class) // 網(wǎng)絡(luò)問題重試
            .retry(DeadlockLoserDataAccessException.class) // 數(shù)據(jù)庫死鎖重試
            .skipLimit(100)
            .skip(DataIntegrityViolationException.class) // 數(shù)據(jù)問題跳過
            .skip(InvalidDataAccessApiUsageException.class)
            .noRollback(ValidationException.class) // 驗證異常不回滾
            .listener(newErrorLogListener()) // 自定義監(jiān)聽器
            .build();
}

// 錯誤日志監(jiān)聽器示例
publicclassErrorLogListenerimplementsItemProcessListener<Input, Output> {
    @Override
    publicvoidonProcessError(Input item, Exception e){
        ErrorLog log = new ErrorLog();
        log.setItemData(item.toString());
        log.setErrorMsg(e.getMessage());
        errorLogRepository.save(log);
    }
}

2. 性能優(yōu)化策略(千萬級數(shù)據(jù)處理)

策略1:并行Step執(zhí)行

圖片圖片

配置代碼:

@Bean
public Job parallelJob(){
    return jobBuilderFactory.get("parallelJob")
            .start(step1())
            .split(new SimpleAsyncTaskExecutor()) // 啟用異步執(zhí)行器
            .add(step2(), step3())
            .build();
}
策略2:分區(qū)處理(Partitioning)

圖片圖片

  • 分區(qū)處理器實現(xiàn):
@Bean
public Step masterStep(){
    return stepBuilderFactory.get("masterStep")
            .partitioner("slaveStep", partitioner())
            .gridSize(10) // 分區(qū)數(shù)量=CPU核心數(shù)*2
            .taskExecutor(new ThreadPoolTaskExecutor())
            .build();
}

@Bean
public Partitioner partitioner(){
    returnnew Partitioner() {
        @Override
        public Map<String, ExecutionContext> partition(int gridSize){
            Map<String, ExecutionContext> result = new HashMap<>();
            long total = getTotalRecordCount();
            
            long range = total / gridSize;
            for (int i = 0; i < gridSize; i++) {
                ExecutionContext context = new ExecutionContext();
                context.putLong("min", i * range);
                context.putLong("max", (i+1) * range);
                result.put("partition"+i, context);
            }
            return result;
        }
    };
}

// Slave Step配置
@Bean
public Step slaveStep(){
    return stepBuilderFactory.get("slaveStep")
            .<Record, Result>chunk(1000)
            .reader(rangeReader(null, null))
            .processor(processor())
            .writer(writer())
            .build();
}

@StepScope
@Bean
public ItemReader<Record> rangeReader(
        @Value("#{stepExecutionContext[min]}") Long min,
        @Value("#{stepExecutionContext[max]}") Long max) {
    returnnew JdbcCursorItemReaderBuilder<Record>()
            .sql("SELECT * FROM records WHERE id BETWEEN ? AND ?")
            .preparedStatementSetter(ps -> {
                ps.setLong(1, min);
                ps.setLong(2, max);
            })
            // 其他配置...
            .build();
}
  • 策略3:異步ItemProcessor

圖片圖片

  • 異步處理配置:
@Bean
public Step asyncStep(){
    return stepBuilderFactory.get("asyncStep")
            .<Input, Output>chunk(1000)
            .reader(reader())
            .processor(asyncItemProcessor())
            .writer(writer())
            .build();
}

@Bean
public AsyncItemProcessor<Input, Output> asyncItemProcessor(){
    AsyncItemProcessor<Input, Output> asyncProcessor = new AsyncItemProcessor<>();
    asyncProcessor.setDelegate(syncProcessor()); // 同步處理器
    asyncProcessor.setTaskExecutor(new ThreadPoolTaskExecutor());
    return asyncProcessor;
}

@Bean
public AsyncItemWriter<Output> asyncItemWriter(){
    AsyncItemWriter<Output> asyncWriter = new AsyncItemWriter<>();
    asyncWriter.setDelegate(syncWriter()); // 同步寫入器
    return asyncWriter;
}

3. 性能對比測試數(shù)據(jù)

圖片圖片

優(yōu)化技巧:

  • 數(shù)據(jù)庫連接池調(diào)優(yōu):
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.minimum-idle=5
  • JVM參數(shù)優(yōu)化:
java -jar -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 ...
  • 批處理參數(shù)調(diào)整:
.chunk(2000) // 根據(jù)內(nèi)存容量調(diào)整
.setQueryTimeout(60) // 數(shù)據(jù)庫查詢超時

六、監(jiān)控與管理(生產(chǎn)級方案)

1. 監(jiān)控方案升級(Spring Batch Admin替代方案)

圖片圖片

  • 現(xiàn)代監(jiān)控棧配置:
// 添加監(jiān)控依賴
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
// 暴露監(jiān)控端點
@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags(){
    return registry -> registry.config().commonTags("application", "batch-service");
}

// 自定義Batch指標
publicclassBatchMetricsListenerextendsJobExecutionListenerSupport{
    privatefinal Counter processedRecords = Counter.builder("batch.records.processed")
            .description("Total processed records")
            .register(Metrics.globalRegistry);
    
    @Override
    publicvoidafterStep(StepExecution stepExecution){
        processedRecords.increment(stepExecution.getWriteCount());
    }
}

2. 元數(shù)據(jù)表結(jié)構(gòu)詳解

圖片圖片

關(guān)鍵表用途:

  • BATCH_JOB_INSTANCE:作業(yè)指紋庫(相同參數(shù)只能存在一個實例)
  • BATCH_JOB_EXECUTION_PARAMS:存儲每次運行的參數(shù)
  • BATCH_STEP_EXECUTION_CONTEXT:保存步驟上下文數(shù)據(jù)(重啟恢復(fù)的關(guān)鍵)

3. 自定義監(jiān)控看板

-- 常用監(jiān)控SQL示例
-- 最近5次作業(yè)執(zhí)行情況
SELECT j.JOB_NAME, e.START_TIME, e.END_TIME, 
       TIMEDIFF(e.END_TIME, e.START_TIME) ASDURATION,
       s.READ_COUNT, s.WRITE_COUNT
FROM BATCH_JOB_EXECUTION e
JOIN BATCH_JOB_INSTANCE j ON e.JOB_INSTANCE_ID = j.JOB_INSTANCE_ID
JOIN BATCH_STEP_EXECUTION s ON e.JOB_EXECUTION_ID = s.JOB_EXECUTION_ID
ORDERBY e.START_TIME DESCLIMIT5;

七、常見問題Q&A(終極指南)

1. 內(nèi)存溢出問題深度解決方案

場景:處理10GB CSV文件時OOM

圖片圖片

  • 優(yōu)化代碼示例:
@Bean
@StepScope
public FlatFileItemReader<LargeRecord> largeFileReader(
        @Value("#{jobParameters['filePath']}") String filePath) {
    
    returnnew FlatFileItemReaderBuilder<LargeRecord>()
            .resource(new FileSystemResource(filePath))
            .lineMapper(new DefaultLineMapper<>() {{
                setLineTokenizer(new DelimitedLineTokenizer());
                setFieldSetMapper(new BeanWrapperFieldSetMapper<>() {{
                    setTargetType(LargeRecord.class);
                }});
            }})
            .linesToSkip(1)
            .strict(false) // 允許文件結(jié)尾空行
            .saveState(false) // 禁用狀態(tài)保存
            .build();
}

// JVM參數(shù)建議
// -XX:+UseG1GC -Xmx2g -XX:MaxGCPauseMillis=200

2. 定時任務(wù)高級配置

  • 多任務(wù)調(diào)度方案:
@Configuration
@EnableScheduling
publicclassScheduleConfig{

    @Autowiredprivate JobLauncher jobLauncher;
    @Autowiredprivate Job reportJob;
    
    // 工作日凌晨執(zhí)行
    @Scheduled(cron = "0 0 2 * * MON-FRI")
    publicvoiddailyJob()throws Exception {
        JobParameters params = new JobParametersBuilder()
                .addString("date", LocalDate.now().toString())
                .toJobParameters();
        jobLauncher.run(reportJob, params);
    }

    // 每小時輪詢
    @Scheduled(fixedRate = 3600000)
    publicvoidpollJob(){
        if(checkNewDataExists()) {
            jobLauncher.run(dataProcessJob, new JobParameters());
        }
    }
    
    // 優(yōu)雅停止示例
    publicvoidstopJob(Long executionId){
        JobExecution execution = jobExplorer.getJobExecution(executionId);
        if(execution.isRunning()) {
            execution.setStatus(BatchStatus.STOPPING);
            jobRepository.update(execution);
        }
    }
}

3. 高頻問題集錦

Q:如何重新運行失敗的任務(wù)?

-- 步驟1:查詢失敗的任務(wù)ID
SELECT * FROM BATCH_JOB_EXECUTION WHERESTATUS = 'FAILED';

-- 步驟2:使用相同參數(shù)重新啟動
JobParameters params = new JobParametersBuilder()
        .addLong("restartId", originalExecutionId)
        .toJobParameters();
jobLauncher.run(job, params);

Q:處理過程中斷電怎么辦?

圖片圖片

Q:如何實現(xiàn)動態(tài)參數(shù)傳遞?

// 命令行啟動方式
java -jar batch.jar --spring.batch.job.name=dataImportJob date=2023-10-01

// 編程式參數(shù)構(gòu)建
publicvoidrunJobWithParams(Map<String, Object> params){
    JobParameters jobParams = new JobParametersBuilder()
            .addString("mode", "forceUpdate")
            .addLong("timestamp", System.currentTimeMillis())
            .toJobParameters();
    jobLauncher.run(importJob, jobParams);
}

4. 性能調(diào)優(yōu)檢查清單

數(shù)據(jù)庫優(yōu)化
  • 添加批量處理索引
  • 配置連接池參數(shù)
  • 啟用JDBC批處理模式
JVM優(yōu)化
-XX:+UseStringDeduplication
-XX:+UseCompressedOops
-XX:MaxMetaspaceSize=512m
Batch配置
spring.batch.jdbc.initialize-schema=never
spring.batch.job.enabled=false
spring.jpa.open-in-view=false


責(zé)任編輯:武曉燕 來源: 碼猿技術(shù)專欄
相關(guān)推薦

2025-10-14 09:12:49

2025-08-04 09:33:42

2022-12-29 08:43:43

項目接口請求

2025-06-05 00:00:00

項目接口合并

2021-03-08 08:02:40

IDEA插件JSON

2021-02-02 15:38:19

Disruptor緩存Java

2025-04-29 08:00:36

2022-02-23 11:47:57

CharlesFiddler抓包

2021-03-26 15:18:11

代碼工具Mockoon

2025-07-23 09:34:24

2025-09-01 01:25:00

SpringMVC注解

2021-05-31 09:02:55

KPI考核工具公司

2022-01-27 08:12:50

Potplayer播放器

2025-05-09 08:40:42

插件頁面Vite

2025-09-08 09:58:06

2009-06-18 15:40:07

Spring Batc

2025-03-03 10:04:49

2020-12-11 11:26:47

Spring批處理重試

2025-10-10 01:00:00

2022-08-02 20:47:38

Spring框架應(yīng)用程序
點贊
收藏

51CTO技術(shù)棧公眾號

国产日产一区二区| 欧美一区二区三区图| 亚洲综合av影视| 午夜视频在线观看国产| 一本色道久久综合无码人妻| 欧美做受69| 国产精品白丝av| 伊人久久久久久久久久| 91视频 -- 69xx| 内射后入在线观看一区| 欧美日韩中文| 日韩欧美国产综合| 亚洲国产精品影视| 一本一道精品欧美中文字幕| 欧美午夜国产| 伊人久久久久久久久久| 超碰caoprom| 久草在线视频网站| 国产成人超碰人人澡人人澡| 久久精品视频99| 五月婷婷之婷婷| www.av在线| 日韩经典一区二区| 亚洲欧美www| 男人亚洲天堂网| 可以免费看污视频的网站在线| 日韩五码在线| 亚洲精品少妇网址| 日韩精品一区中文字幕| 国产高清视频在线观看| 日韩国产在线观看| 久久久人成影片一区二区三区| 精品无码av一区二区三区不卡| 国产最新在线| 日本一区二区三区四区在线视频| 国产精品男人的天堂| 欧美色图17p| 白嫩亚洲一区二区三区| 一区二区三区自拍| 99视频在线| 影音先锋亚洲天堂| 国产成人ay| 欧美美女视频在线观看| 国产树林野战在线播放| 精品人妻无码一区二区色欲产成人 | 四川一级毛毛片| av毛片在线播放| 成人高清在线视频| 午夜精品三级视频福利| 在线免费看黄视频| 99久久婷婷国产综合精品首页| 国产精品麻豆99久久久久久| 91免费欧美精品| 国产亚洲精品久久久久久无几年桃 | 午夜日韩在线电影| 免费精品视频一区| 中文字幕观看视频| 欧美久色视频| 久精品免费视频| 久久精品一区二区免费播放| 久草综合在线| 午夜私人影院久久久久| a级片一区二区| 手机福利小视频在线播放| 日韩福利电影在线观看| 国产脚交av在线一区二区| 四虎影院中文字幕| 亚洲瘦老头同性70tv| 欧美精品久久99| 182午夜视频| 亚洲少妇视频| 亚洲欧美日韩国产另类专区| 久久亚洲高清| 国产女人在线观看| 中文字幕欧美激情一区| 国产伦精品一区二区三毛| 最近中文字幕免费在线观看| 雨宫琴音一区二区在线| 中文字幕亚洲精品| 欲求不满的岳中文字幕| 粉嫩av国产一区二区三区| 午夜精品一区二区三区三上悠亚| 免费av手机在线观看| 免费在线午夜视频| 国产欧美日韩不卡免费| 天天综合中文字幕| 国产天堂素人系列在线视频| 国产精品美女视频| 男人天堂新网址| 在线精品亚洲欧美日韩国产| 亚洲综合在线观看视频| 一区二区视频在线播放| 91香蕉在线观看| 精品国产91久久久| 日韩一级性生活片| 在线免费观看的av| 精品国产福利视频| 天天干天天草天天| 日韩高清不卡| 色综合久久久久久久| 奇米影视亚洲色图| 国产美女一区视频| 亚洲一区二区偷拍精品| 蜜桃视频一区二区在线观看| 黄色软件视频在线观看| 亚洲成a人v欧美综合天堂下载 | 九九热最新地址| 精品久久成人| 亚洲网站在线播放| 无码h肉动漫在线观看| 热久久天天拍国产| 在线播放日韩专区| 国产在线综合网| 亚洲婷婷在线| 国内精品久久久久影院 日本资源| 亚洲欧美偷拍视频| 肉肉av福利一精品导航| 91九色极品视频| 丰满大乳国产精品| 成人国产免费视频| 热这里只有精品| 日韩免费va| 欧美日韩一区二区三区免费看| 亚洲污视频在线观看| 欧美视频免费看| 亚洲激情视频在线| 这里只有久久精品| 一区久久精品| 97神马电影| 黄色在线免费看| 欧美色偷偷大香| 亚洲精品国产一区黑色丝袜| 成人精品影院| 日本久久久久久| 中国一级片黄色一级片黄| eeuss影院一区二区三区| 久久久久久九九| 超碰在线影院| 91久久久免费一区二区| 久久婷婷综合色| 精品国产乱码久久久久久果冻传媒 | 国产精品普通话对白| 日本免费在线精品| 手机看片福利在线观看| 欧美性xxxxx极品娇小| 国产又粗又长又大的视频| 91麻豆精品国产综合久久久| 中文字幕国产精品| 久久精品视频2| 国产精品资源在线| 亚洲色图都市激情| 亚洲综合电影| 日韩精品免费在线视频| 性色国产成人久久久精品| 欧美体内she精视频在线观看| 91精品久久久久久综合乱菊| 黄色aaa毛片| 夜夜嗨av一区二区三区中文字幕| 中文写幕一区二区三区免费观成熟| 欧美成人午夜77777| 97视频色精品| 欧美高清电影在线| 欧美影视一区在线| 无码人妻精品一区二区三| 韩国久久久久| 九九99玖玖| h网站久久久| 91成人在线观看喷潮| 亚洲欧美日本一区二区| 91精品国产福利在线观看麻豆| 国内久久久精品| 色吊丝在线永久观看最新版本| 亚洲视频免费看| 青青青国产在线视频| 精品国产午夜肉伦伦影院| 久久国产精品偷| 在线观看毛片视频| 亚洲精品大片www| 欲求不满的岳中文字幕| 日韩国产高清在线| 亚洲国产成人不卡| 欧美黑人一区| 久久伊人免费视频| 伊人久久久久久久久久久久| 一区在线观看视频| 亚洲一区二区蜜桃| 久久久久国产精品| 精品一区2区三区| 国产精品99精品一区二区三区∴| 久久午夜a级毛片| 五月婷婷久久久| 午夜影院久久久| 国产视频123区| 粉嫩一区二区三区性色av| 免费裸体美女网站| 自产国语精品视频| 成人激情av在线| 草草在线观看| 久久久精品电影| 蜜桃视频在线观看视频| 欧美一级二级三级蜜桃| 欧美视频www| 91蜜桃免费观看视频| 一二三四视频社区在线| 久久综合99| 国产主播精品在线| 国产婷婷视频在线| 亚洲精品少妇网址| 成人免费观看在线视频| 欧美无砖专区一中文字| 日韩无码精品一区二区三区| 成人免费看的视频| 妞干网在线视频观看| 91亚洲国产| 免费观看成人在线| 风间由美一区二区av101| 久久久久久久久国产精品| 人妻无码中文字幕| 欧美久久一二三四区| 亚洲第一网站在线观看| 中文字幕乱码日本亚洲一区二区 | 久久精品成人欧美大片古装| 四虎成人免费在线| 亚洲成色777777女色窝| 日韩少妇高潮抽搐| 亚洲精品免费在线观看| 最新中文字幕视频| 99综合电影在线视频| 国产大学生av| 国产精品77777| 国产免费黄色小视频| 午夜久久福利| 久久成人资源| 久久精品色综合| 国产乱码精品一区二区三区日韩精品| 精品国产亚洲一区二区三区大结局 | 欧洲亚洲国产日韩| 丁香六月婷婷综合| 中文字幕第一区| 无码h肉动漫在线观看| 91色婷婷久久久久合中文| 日韩免费高清一区二区| 不卡在线观看av| 免费黄色三级网站| 99re成人在线| 国产欧美一区二| 麻豆国产精品视频| 日日摸日日碰夜夜爽无码| 欧美日韩亚洲一区| www.夜夜爱| 欧美精品久久久久久| 日本一区视频在线观看| 婷婷综合国产| 国产激情综合五月久久| 日韩电影免费观| 国产精品入口福利| 24小时成人在线视频| 亚洲综合自拍一区| 风间由美性色一区二区三区四区| 国产一区二区高清不卡| 成人精品国产亚洲| 国产精品最新在线观看| 麻豆视频在线观看免费网站黄| 中国日韩欧美久久久久久久久| yw在线观看| 久久精品精品电影网| 在线中文字幕-区二区三区四区| 欧美激情一区二区三区在线视频观看 | 精品国产一区二区三区久久久| 日本波多野结衣在线| 精品亚洲一区二区三区在线观看 | 国内精品久久久久久久影视简单 | 国产成人在线网址| 一区二区三区在线免费视频| 成年人免费看毛片| 欧美在线小视频| 国产99久久九九精品无码免费| 色天天综合色天天久久| 一区二区三区日| 欧美在线制服丝袜| 国产肥老妇视频| 日韩成人av网址| 日本在线播放| 中文字幕日韩视频| 亚洲综合图区| 在线国产精品播放| 天使と恶魔の榨精在线播放| 91tv亚洲精品香蕉国产一区7ujn| 中文字幕中文字幕在线十八区| 国模私拍视频一区| 精品176极品一区| 国产精品亚洲一区| 日本黄色精品| 亚洲美免无码中文字幕在线| 日av在线不卡| 国产精品69页| 福利一区二区在线| 毛片aaaaaa| 中文字幕精品在线不卡| 久久久久成人网站| 精品视频在线免费观看| 人妻精品无码一区二区| 色综合伊人色综合网| 98在线视频| 色悠悠久久久久| 国产在线精彩视频| 91天堂在线观看| 精品在线手机视频| 日本精品国语自产拍在线观看| 最新国产精品视频| 欧美日韩视频免费| 老鸭窝一区二区久久精品| 欧美在线一级片| 亚洲精品五月天| 在线观看免费高清视频| 亚洲精品视频久久| 成人三级高清视频在线看| 91亚洲精品视频| 精品日本12videosex| 国产精品专区在线| 成人在线视频首页| 一起操在线播放| 欧美女孩性生活视频| 国产一区二区影视| xxx欧美精品| 日本高清不卡一区二区三区视频 | 视频一区在线| 一区二区视频国产| 理论片日本一区| 国产三级黄色片| 日本精品一区二区三区高清| 性xxxfllreexxx少妇| 久久久视频精品| 大型av综合网站| 久久人人爽人人爽人人av| 国产精品一卡二卡在线观看| 日本高清一二三区| 欧美高清hd18日本| 老司机在线视频二区| 国产在线不卡精品| 婷婷综合视频| 和岳每晚弄的高潮嗷嗷叫视频| 国产一区二区在线影院| 亚洲激情 欧美| 亚洲国产日日夜夜| 中国a一片一级一片| 亚洲日韩第一页| 色网在线观看| 国产精华一区| 日韩免费高清| 一区二区传媒有限公司| www.激情成人| 少妇一级淫片免费放中国 | 日本一区二区视频在线播放| 亚洲天堂网中文字| 99精品免费观看| 亚洲欧美成人在线| 性欧美1819sex性高清| 日韩伦理一区二区三区av在线| 日本欧美大码aⅴ在线播放| 刘亦菲国产毛片bd| 91精品国产91热久久久做人人| 香蕉人妻av久久久久天天| 57pao成人国产永久免费| 精品一区三区| 久久久久xxxx| 亚洲一区二区三区视频在线| 人妻偷人精品一区二区三区| 欧美一二三视频| 久久精品国产99久久| 日本r级电影在线观看| 午夜精品久久久久久久久久| 理论视频在线| 91视频网页| 先锋影音久久久| 岛国精品一区二区三区| 国产精品福利在线播放| 中文字幕在线日本| 日韩在线观看你懂的| 成人春色在线观看免费网站| 在线播放豆国产99亚洲| 国产成都精品91一区二区三| 日产欧产va高清| 日韩欧美色电影| 北岛玲heyzo一区二区| 影音先锋在线亚洲| 99视频一区二区三区| 中文字幕视频二区| 性色av一区二区三区在线观看| 国产免费av一区二区三区| 黄色一级片免费播放| 国产精品电影一区二区三区| www夜片内射视频日韩精品成人| 2019中文字幕在线免费观看| 色琪琪久久se色| 噜噜噜在线视频| 福利二区91精品bt7086| 亚洲av成人精品一区二区三区在线播放| 国产精品18久久久久久麻辣| 欧美精品一区二区三区久久久竹菊|