专栏名称: Java基基
一个苦练基本功的 Java 公众号,所以取名 Java 基基
目录
相关文章推荐
Datawhale  ·  大模型微调(Fine-Tuning)全流程思考 ·  2 天前  
企名片  ·  城市创新先锋|广州市25家独角兽企业 ·  3 天前  
企名片  ·  城市创新先锋|广州市25家独角兽企业 ·  3 天前  
AIGC开放社区  ·  超过Sora!谷歌发布Veo ... ·  5 天前  
AIGC开放社区  ·  超过Sora!谷歌发布Veo ... ·  5 天前  
楼主说楼市  ·  无人机能不能救下那个女孩? ·  5 天前  
51好读  ›  专栏  ›  Java基基

性能爆表:SpringBoot利用ThreadPoolTaskExecutor批量插入百万级数据实测!

Java基基  · 公众号  · 后端 科技自媒体  · 2024-09-13 11:55

主要观点总结

文章介绍了社群交流和开源项目相关信息,包括多个主题如面试指导、简历优化、开源项目介绍等。文章还详细描述了如何利用多线程技术提高数据插入效率,包括具体实现细节和测试结果。

关键观点总结

关键观点1: 社群交流及资源

文章提供了关于社群交流的信息,包括一对一交流、面试小册、简历优化等。读者可以加入相关社群获取资源。

关键观点2: 开源项目及介绍

文章介绍了一个开源项目,包括其功能和实现细节,并提供了项目地址和视频教程链接。

关键观点3: 异步线程配置及实现

文章详细描述了如何通过配置异步线程来提高数据插入效率,包括核心线程数、最大线程数、队列大小等配置信息的设定。同时提供了创建异步线程和业务类的代码示例。

关键观点4: 多线程批量插入的测试

文章展示了多线程批量插入的具体测试方法和结果,包括模拟大量数据的测试、不同线程数的测试以及数据完整性的检查。总结了测试结果,并给出了测试电脑的配置信息。


正文

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入芋道快速开发平台知识星球。下面是星球提供的部分资料: 

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro
  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud
  • 视频教程:https://doc.iocoder.cn
【国内首批】支持 JDK 21 + SpringBoot 3.2.0、JDK 8 + Spring Boot 2.7.18 双版本 

来源:azdebug.blog.csdn.net/
article/details/103697108

前言

  • 开发目的: 提高百万级数据插入效率。
  • 采取方案: 利用ThreadPoolTaskExecutor多线程批量插入。
  • 采用技术: springboot2.1.1+mybatisPlus3.0.6+swagger2.5.0+Lombok1.18.4+postgresql+ThreadPoolTaskExecutor等。

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro
  • 视频教程:https://doc.iocoder.cn/video/

具体实现细节

application-dev.properties添加线程池配置信息


> 基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
>
> * 项目地址:
> * 视频教程:

# 异步线程配置
# 配置核心线程数
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:当pool已经达到max size的时候,如何处理新任务
        // 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 logOutputResults, LogOutputResultMapper logOutputResultMapper, CountDownLatch countDownLatch) {
        try{
            log.warn("start executeAsync");
            //异步线程要做的事情
            logOutputResultMapper.addLogOutputResultBatch(logOutputResults);
            log.warn("end executeAsync");
        }finally {
            countDownLatch.countDown();// 很关键, 无论上面程序是否异常必须执行countDown,否则await无法释放
        }
    }
}

创建多线程批量插入具体业务方法

@Override
    public int testMultiThread() {
        List logOutputResults = getTestData();
        //测试每100条数据插入开一个线程
        List> lists = ConvertHandler.splitList(logOutputResults, 100);
        CountDownLatch countDownLatch = new CountDownLatch(lists.size());
        for (List listSub:lists) {
            asyncService.executeAsync(listSub, logOutputResultMapper,countDownLatch);
        }
        try {
            countDownLatch.await(); //保证之前的所有的线程都执行完成,才会走下面的;
            // 这样就可以在下面拿到所有线程执行完的集合结果
        } catch (Exception e) {
            log.error("阻塞异常:"+e.getMessage());
        }
        return logOutputResults.size();
    }

模拟2000003 条数据进行测试

多线程 测试 2000003  耗时如下:耗时1.67分钟

本次开启30个线程,截图如下:

单线程测试2000003  耗时如下:耗时5.75分钟

检查多线程入库的数据,检查是否存在重复入库的问题:

根据id分组,查看是否有id重复的数据,通过sql语句检查,没有发现重复入库的问题

检查数据完整性:通过sql语句查询,多线程录入数据完整

测试结果

不同线程数测试:

总结

通过以上测试案列,同样是导入2000003  条数据,多线程耗时1.67分钟,单线程耗时5.75分钟。通过对不同线程数的测试,发现不是线程数越多越好,具体多少合适,网上有一个不成文的算法:

CPU核心数量*2 +2 个线程。

附:测试电脑配置



欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。

谢谢支持哟 (*^__^*)