【Java】SpringBoot使用@Sync注解 异步批量插入百万条数据
多线程方式可以提高插入数据的效率,网络对数据新增的影响也很大。如下:写了一个简单案例,创建了100w条数据,然后调用。然后进行分割每1000条一组,然后交给异步方法进行调用。对于IO密集型 更多的线程池对于提升速度效果不明显。进行线程阻塞,这样方便查看所有线程执行完成的耗时。是不推荐使用的,但是我们可以自定义线程池。一般springBoot默认自带的。线程池方式对于提升速度有显著效果。同样是创建了
·
单线程方式
批量插入数据的简单案例
如下:写了一个简单案例,创建了100w条数据,然后调用mybatis-plus
批量新增方法
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.StopWatch;
@Slf4j
@RestController
public class TestThreadController {
@Resource
private EventInfoService eventInfoService;
@GetMapping("/add")
public String add(){
StopWatch stopWatch = new StopWatch();
log.info("开始....");
List<TEventInfo> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
list.add(new TEventInfo(i+"","1","消息内容"+i));
}
stopWatch.start();
eventInfoService.saveBatch(list);
stopWatch.stop();
log.info("单线程耗时:{}ms",stopWatch.getLastTaskTimeMillis());
return "success";
}
}
异步线程池方式
同样是创建了100w条数据
然后进行分割每10000条一组,然后交给异步方法进行调用
主线程创建了CountDownLatch
使用latch.await()
进行线程阻塞,这样方便查看所有线程执行完成的耗时
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.StopWatch;
@Slf4j
@RestController
public class TestThreadController {
@Resource
private EventInfoService eventInfoService;
@GetMapping("/add2")
public String add2(){
StopWatch stopWatch = new StopWatch();
log.info("开始....");
List<TEventInfo> list = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
list.add(new TEventInfo(i+"","1","消息内容"+i));
}
List<List<TEventInfo>> lists = ListUtil.partition(list, 10000);
CountDownLatch latch = new CountDownLatch(lists.size());
// 计时开始
stopWatch.start();
for (List<TEventInfo> infoList : lists) {
eventInfoService.saveAll(infoList,latch);
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 计时结束
stopWatch.stop();
log.info("线程池耗时:{}ms,{}ns",stopWatch.getLastTaskTimeMillis() ,stopWatch.getLastTaskTimeNanos());
return "success";
}
}
使用注解标记方法使用异步@Async("asyncExecutor")
@Service
public class EventInfoServiceImpl extends ServiceImpl<EventInfoMapper, EventInfo> implements EventInfoService {
// 实现类还是调用了批量新增的方法
// 不过传入的CountDownLatch 方便检测整个线程池跑完要花费多少时间
@Override
@Async("asyncExecutor")
@Transactional(rollbackFor = Exception.class)
public void saveAll(List<EventInfo> infoList, CountDownLatch latch) {
try {
saveBatch(infoList);
} finally {
latch.countDown();
}
}
}
一般springBoot默认自带的@Async
是不推荐使用的,但是我们可以自定义线程池
@Configuration
public class ThreadPoolConfig {
@Bean
public ThreadPoolTaskExecutor asyncExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// IO 密集型:cpu核心x2 + 1
executor.setCorePoolSize(33);
// IO 密集型:cpu核心x4
executor.setMaxPoolSize(66);
// 队列容量
executor.setQueueCapacity(120);
// 线程空闲时间
executor.setKeepAliveSeconds(30);
// 给每个线程池名称设置前缀
executor.setThreadNamePrefix("zxh-");
executor.initialize();
return executor;
}
}
启动类上需要加上这个注解@EnableAsync
@EnableAsync
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(IpSourceApplication.class, args);
}
}
测试结果
多线程方式可以提高插入数据的效率,网络对数据新增的影响也很大。
关闭mybaties
日志输出
// 单线程
INFO 28320 --- [nio-8080-exec-4] c.z.ip.controller.TestThreadController : 开始....
INFO 28320 --- [nio-8080-exec-4] c.z.ip.controller.TestThreadController : 单线程耗时:87568ms
// 线程池 核心线程数33 最大线程数66
INFO 28320 --- [nio-8080-exec-2] c.z.ip.controller.TestThreadController : 开始....
INFO 28320 --- [nio-8080-exec-2] c.z.ip.controller.TestThreadController : 线程池耗时:30268ms
// 线程池 核心线程数100 最大线程数200
INFO 29844 --- [nio-8080-exec-1] c.z.ip.controller.TestThreadController : 开始....
INFO 29844 --- [nio-8080-exec-1] c.z.ip.controller.TestThreadController : 线程池耗时:28153ms
结论
线程池方式对于提升速度有显著效果
对于IO密集型 更多的线程池对于提升速度效果不明显

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)