精品欧美一区二区三区在线观看 _久久久久国色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ù)棧公眾號

一区二区三区免费在线看| 免费在线超碰| 亚洲激情网址| 国产亚洲欧美一区| 一级做a爱视频| 国产高清中文字幕在线| 国产精品午夜春色av| 肥熟一91porny丨九色丨| 成人精品在线看| 先锋资源久久| 亚洲色图13p| 中文字幕久久久久久久| 亚洲wwww| 午夜精品影院在线观看| 亚洲一区二区精品在线| 日本黄色大片视频| 国产最新精品免费| 国产成人自拍视频在线观看| 久久久久人妻一区精品色欧美| 美女毛片一区二区三区四区| 日韩视频免费观看高清完整版| 91精品91久久久中77777老牛| 大片免费在线看视频| 国产亚洲欧美一区在线观看| 成人免费看片网址| 91theporn国产在线观看| 亚洲欧美春色| 国内精品久久久久久久| 中文字幕五月天| 日韩欧美综合| 亚洲午夜未删减在线观看| 国产麻豆剧传媒精品国产| 男女啪啪999亚洲精品| 欧美日韩一区免费| 欧美成人三级在线视频| av在线官网| 国产精品久久久久久久午夜片 | 香蕉影院在线观看| 国产精品二区影院| 欧美成人第一页| 欧美另类videoxo高潮| 日本一区二区高清不卡| 亚洲一区二区久久| 日韩精品无码一区二区三区久久久| 国产成人精品亚洲线观看| 日韩一区和二区| 欧美丝袜在线观看| 欧美亚洲人成在线| 欧美日本一区二区| 最新天堂中文在线| 福利精品一区| 欧美三级蜜桃2在线观看| 老熟妇仑乱视频一区二区| 91av亚洲| 欧美日韩中文另类| 成人日韩在线视频| aa亚洲一区一区三区| 欧美精品电影在线播放| 亚洲一级片av| 永久免费精品视频| 精品成人免费观看| 美女又爽又黄视频毛茸茸| 欧洲在线一区| 亚洲欧美制服丝袜| 亚洲一级理论片| 五月精品视频| 九色精品免费永久在线| 久久影院一区二区| 国产精品三上| 国产精品18久久久久久首页狼 | 午夜激情视频在线播放| 一区二区三区四区电影| 欧美激情精品久久久久久大尺度| 欧美一级高潮片| 国产精品亚洲欧美| 国产精品视频免费在线| 国产精品无码久久久久成人app| 国产一区二区在线观看视频| 99久久免费国| 欧美亚洲日本| 亚洲欧洲国产日韩| 国产96在线 | 亚洲| 成人做爰视频www网站小优视频| 欧美日韩免费视频| 不许穿内裤随时挨c调教h苏绵 | 国产一区二区三区免费观看| 古典武侠综合av第一页| 嫩草精品影院| 亚洲女与黑人做爰| 国产成人无码a区在线观看视频| 亚洲日本网址| 日韩久久免费av| 亚洲黄色在线网站| 国产精品99久久久久久动医院| 欧美激情视频一区二区三区不卡| 国产美女激情视频| 国产在线播放一区三区四| 精品免费视频123区| 最新国产在线观看| 精品福利免费观看| 天堂在线一区二区三区| 欧美人与动xxxxz0oz| 色青青草原桃花久久综合| 日韩和一区二区| 精品一区二区三区久久| 久久久亚洲综合网站| 黄色免费在线观看| 日本高清免费不卡视频| 人妻 丝袜美腿 中文字幕| 欧美日韩国产在线观看网站| 欧美激情亚洲一区| 在线播放亚洲精品| 久久久久久久久久看片| 少妇大叫太大太粗太爽了a片小说| 激情亚洲影院在线观看| 亚洲国产黄色片| 欧美国产日韩在线观看成人| 日本不卡在线视频| 精品乱子伦一区二区三区| 欧美人动性xxxxz0oz| 精品视频一区三区九区| japanese中文字幕| 国产视频一区三区| 69174成人网| 色网站在线看| 91成人免费电影| 国产 中文 字幕 日韩 在线| 欧美女人交a| 91欧美精品成人综合在线观看| 欧美日韩伦理片| 精品久久久久久中文字幕大豆网 | 欧美国产日韩一区| 91tv国产成人福利| 国产精品久久久久精k8| 最新中文字幕免费视频| 国产日产精品_国产精品毛片| 57pao国产精品一区| 欧美亚洲精品在线观看| 亚洲国产aⅴ成人精品无吗| 丰满人妻一区二区三区53视频| 天天精品视频| 亚洲精品欧美极品| av官网在线播放| 日韩一级片网站| 久草免费新视频| 成人一区二区三区在线观看| 国产美女作爱全过程免费视频| 涩涩屋成人免费视频软件| 九九久久综合网站| 成人无码一区二区三区| 亚洲成人tv网| 北岛玲一区二区| 国产精品人人爽人人做我的可爱| 久久av二区| 新版的欧美在线视频| 亚洲乱码av中文一区二区| 毛片基地在线观看| 国产女同性恋一区二区| 一个色综合久久| 欧美国内亚洲| 国产自产精品| 三上悠亚激情av一区二区三区| 国产午夜精品全部视频播放| 亚洲午夜在线播放| 亚洲精品久久久久久国产精华液| 中文字幕一区二区三区人妻在线视频 | crdy在线观看欧美| 欧美激情视频给我| 免费在线一级视频| 欧美日韩高清一区二区| 久草视频在线资源| 久久免费视频色| 污污的网站免费| 在线精品在线| 欧美午夜欧美| 精品一区二区三区亚洲| 午夜精品久久久久久久男人的天堂| 天堂资源中文在线| 欧美日韩精品三区| 日韩免费一二三区| 国产日韩欧美综合在线| 亚洲视频在线不卡| 中文在线一区| 大桥未久一区二区| 欧美综合精品| 91在线视频免费| 涩涩视频在线免费看| 日韩在线免费视频观看| 少妇又色又爽又黄的视频| 欧美亚洲一区二区在线| 久久久久久久久久久网| 国产亚洲精品福利| 亚洲AV成人精品| 日韩电影免费在线观看网站| 欧美黄网在线观看| 国产尤物久久久| 高清国语自产拍免费一区二区三区| free欧美| 久久人人爽人人| 日韩伦理在线电影| 日韩大片在线观看视频| 国产精品伊人久久| 日韩欧美在线视频观看| 精品一区二区三区四| 中文字幕第一区第二区| 亚洲欧美日韩色| 毛片一区二区三区| 国产高清精品在线观看| 欧美不卡在线| 五月天色一区| 偷拍一区二区| 国产日韩一区二区三区| 成人免费91| 国产精品亚洲视频在线观看| 亚洲精品**中文毛片| 欧美国产第一页| 国产在线看片| 最近更新的2019中文字幕| 青青国产在线| 亚洲韩国欧洲国产日产av| aaa级黄色片| 欧美日韩国产123区| 国产一级片免费视频| 精品国产乱码久久久久酒店| 久久综合加勒比| 樱桃视频在线观看一区| 精品国产国产综合精品| 中文字幕av一区 二区| 国产成人无码精品久久二区三| 成人福利视频在线| 色哟哟无码精品一区二区三区| 国产伦精品一区二区三区免费迷 | 亚洲av片一区二区三区| 精品国产一区二区三区四区四| 国产免费不卡av| 欧美剧情电影在线观看完整版免费励志电影 | 国产乱码一区| 91精品国产自产在线丝袜啪 | 国产精品久久久久一区二区三区厕所| 欧美午夜精品久久久久免费视| 日韩理论电影中文字幕| 精品日韩电影| 日韩精品丝袜美腿| 欧美日韩国产精品一区二区| 奇米狠狠一区二区三区| 日本一区免费在线观看| 国产一区毛片| 亚洲精品一区二区三区樱花| 色婷婷一区二区三区| 午夜啪啪免费视频| 中文在线日韩| www.国产在线视频| 亚洲国产精品第一区二区| 国产精品网站免费| 午夜一级在线看亚洲| 国产99久久九九精品无码| 久久中文精品| 潘金莲激情呻吟欲求不满视频| 精品一区二区久久久| 亚洲综合123| 丁香六月综合激情| av无码av天天av天天爽| 国产日韩一级二级三级| 免费在线观看黄色小视频| 亚洲免费在线观看视频| 日韩精品一区二区三| 一本久久精品一区二区| 中文字幕在线观看国产| 日韩欧美一区二区三区在线| 污污的视频网站在线观看| 亚洲性日韩精品一区二区| 黄网站免费在线观看| 久久久在线观看| 天堂av中文在线观看| 国产精品永久免费在线| 亚洲精品国产九九九| 蜜桃成人在线| 久久精品青草| 人妻精品无码一区二区三区| 蜜乳av一区二区| 美女伦理水蜜桃4| 中文字幕巨乱亚洲| 九九视频免费在线观看| 色呦呦国产精品| 国产婷婷在线视频| 国产视频精品一区二区三区| 欧洲不卡av| 97视频在线观看视频免费视频 | 青春草视频在线| 日本成人精品在线| 亚洲欧美日本国产| 日韩欧美亚洲区| 一区在线免费| www.超碰97.com| 91视频xxxx| 国产suv一区二区三区| 色婷婷综合久久久| 亚洲精品久久久蜜桃动漫| 亚洲欧洲av一区二区| 色yeye免费人成网站在线观看| 国产精品欧美日韩久久| 精品午夜电影| 免费的av在线| 日本美女一区二区| avtt香蕉久久| 亚洲综合激情另类小说区| 中文 欧美 日韩| 精品视频久久久久久| 丝袜在线视频| 成人av在线天堂| 国产精品一区二区99| 免费不卡av在线| 国产精品77777| 少妇视频一区二区| 91国产视频在线观看| 亚洲三区在线播放| 久久理论片午夜琪琪电影网| 久久天堂久久| 在线免费一区| 麻豆成人av在线| 欧美成人另类视频| 欧美性生交xxxxx久久久| 人人妻人人玩人人澡人人爽| 欧美成人免费在线观看| 96sao精品免费视频观看| 亚洲人成网站在线播放2019| 日韩精品电影在线| 人妻熟女aⅴ一区二区三区汇编| 亚洲午夜在线观看视频在线| 精品国产免费无码久久久| 久久av中文字幕| 99视频这里有精品| 国产又大又长又粗又黄| 久久国产日韩欧美精品| 中国1级黄色片| 欧美婷婷六月丁香综合色| 福利在线视频导航| 国产精品美女主播在线观看纯欲| 免费成人网www| 久久久999视频| 26uuu色噜噜精品一区二区| 国产精品黄色大片| 国产视频精品一区二区三区| 欧美大片免费高清观看| 日本精品二区| 另类调教123区| 黄视频网站免费看| 欧美成人乱码一区二区三区| 男女免费观看在线爽爽爽视频| 99久久99久久| 亚洲免费成人| 亚洲黄色免费视频| 欧美色图第一页| 国产原创在线观看| 国产精品久久亚洲| 在线视频免费在线观看一区二区| 中国av免费看| 欧美中文字幕一二三区视频| 成人在线免费观看| 亚洲一区二区三| av不卡在线| 欧美 日韩 国产 成人 在线观看 | 亚洲 欧美 激情 小说 另类| 日本韩国欧美精品大片卡二| 日韩国产欧美一区二区| 亚洲天堂一区二区在线观看| 亚洲国产精品尤物yw在线观看| 亚洲欧美日韩免费| 国产精品久久网| 在线一区免费| 久久久久久久无码| 欧美日韩在线观看一区二区| 亚洲小说区图片区都市| 九色91在线视频| 秋霞影院一区二区| 久草视频在线资源| 亚洲欧美第一页| 激情五月综合婷婷| 国产又黄又大又粗视频| 国产精品免费久久| 好吊视频一二三区| 国产精品第10页| 国产一区美女| www在线观看免费视频| 欧美一区二区精品| 欧美黑人粗大| 亚洲精品国产suv一区88| 久久综合九色综合欧美98| 国产精品久久久久久久久久久久久久久久 | 国产白浆在线免费观看| 亚洲成色www久久网站| 国产iv一区二区三区| 日韩 国产 欧美| 欧美极品xxxx| 久久激情电影| 色天使在线视频| 日韩女优av电影在线观看| 成人在线视频播放| 日韩欧美一区三区| 亚洲欧美区自拍先锋|