SpringBootを利用してThreadPoolTaskExecutorで数百万件のデータを一括挿入する

SpringBootを利用してThreadPoolTaskExecutorで数百万件のデータを一括挿入する

application-dev.properties にスレッドプールの設定情報を追加する

# 非同期スレッド設定
# コアスレッド数を設定する
async.executor.thread.core_pool_size = 30
# 最大スレッド数を設定する
async.executor.thread.max_pool_size = 30
# キューのサイズを設定する
async.executor.thread.queue_capacity = 99988
# スレッドプール内のスレッドの名前の接頭辞を設定する
async.executor.thread.name.prefix = async-importDB-

Spring コンテナにスレッドプールの Bean オブジェクトを注入する

@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {
    @Value("${async.executor.thread.core_pool_size}")
    private int corePoolSize;
    @Value("${async.executor.thread.max_pool_size}")
    private int maxPoolSize;
    @Value("${async.executor.thread.queue_capacity}")
    private int queueCapacity;
    @Value("${async.executor.thread.name.prefix}")
    private String namePrefix;
 
    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        log.warn("start asyncServiceExecutor");
        ThreadPoolTaskExecutor executor = new VisiableThreadPoolTaskExecutor();
        // コアスレッド数を設定する
        executor.setCorePoolSize(corePoolSize);
        // 最大スレッド数を設定する
        executor.setMaxPoolSize(maxPoolSize);
        // キューサイズを設定する
        executor.setQueueCapacity(queueCapacity);
        // スレッドプール内のスレッドの名前の接頭辞を設定する
        executor.setThreadNamePrefix(namePrefix);
        // rejection-policy:プールが最大サイズに達した場合、新しいタスクをどのように処理するか        
		// CALLER_RUNS:新しいスレッドでタスクを実行せず、呼び出し元のスレッドで実行する
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

非同期スレッドを作成するビジネスクラス

@Service
@Slf4j
public class AsyncServiceImpl implements AsyncService {
@Override
    @Async("asyncServiceExecutor")
    public void executeAsync(List<LogOutputResult> logOutputResults, LogOutputResultMapper logOutputResultMapper, CountDownLatch countDownLatch) {
        try{
            log.warn("start executeAsync");
            logOutputResultMapper.addLogOutputResultBatch(logOutputResults);
            log.warn("end executeAsync");
        }finally {
            countDownLatch.countDown();
        }
    }
}

複数スレッドでバッチ挿入を行う具体的なビジネスメソッドを作成する

@Override
public int testMultiThread() {
    List<LogOutputResult> logOutputResults = getTestData();
	//100件のデータごとにスレッドを開いてテストする
	List<List<LogOutputResult>> lists = ConvertHandler.splitList(logOutputResults, 100);
    CountDownLatch countDownLatch = new CountDownLatch(lists.size());
    for (List<LogOutputResult> listSub:lists) {
        asyncService.executeAsync(listSub, logOutputResultMapper,countDownLatch);
    }
	try {
		//以前のすべてのスレッドが実行を完了するまで、次に進まない
        countDownLatch.await();            
 	} catch (Exception e) {
        log.error("ブロッキング例外:"+e.getMessage());
    }
    return logOutputResults.size();
}
Spring Boot中的`ThreadPoolTaskExecutor`是一个线程池管理工具,可以用来执行异步任务,包括批量处理数据。当需要处理大量数据,特别是涉及到数据库的插入操作时,使用线程池可以提高性能,因为它可以并发地执行多个插入操作,减少阻塞。 批量插入百万级数据时,你可以按照以下步骤操作: 1. 配置ThreadPoolTaskExecutor:首先,在Spring Boot应用的配置类中(如`ApplicationConfig`或`MyConfig`),定义一个`ThreadPoolTaskExecutor`,设置核心线程数、最大线程数和队列大小等参数。例如: ```java @Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); // 核心线程数 executor.setMaxPoolSize(20); // 最大线程数 executor.setQueueCapacity(1000); // 队列大小 executor.setThreadNamePrefix("data-import-"); executor.initialize(); return executor; } ``` 2. 创建批量操作类:创建一个包含`Runnable`接口的类,用于封装批量插入操作。比如,如果你的数据源是JPA,可以创建一个`JpaInsertBatch`类: ```java public class JpaInsertBatch implements Runnable { private List<YourEntity> entities; // 你需要插入的数据列表 @Override public void run() { entityManager.persistAll(entities); } } ``` 3. 批量提交任务:使用`ThreadPoolTaskExecutor`来执行批量插入任务,循环提交你的数据列表到线程池: ```java @Autowired private ThreadPoolTaskExecutor taskExecutor; List<YourEntity> largeDataList = ... // 获取你的百万级数据 for (int i = 0; i < largeDataList.size(); i += batchSize) { int endIndex = Math.min(i + batchSize, largeDataList.size()); taskExecutor.execute(new JpaInsertBatch(largeDataList.subList(i, endIndex))); } ``` 4. 评估性能:实测时,记录开始和结束时间,计算整个过程的耗时,并根据实际情况调整线程池参数,以达到最佳性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值