springboot+mybatis快速插入大量數(shù)據(jù)的具體實現(xiàn)
前言
在公司業(yè)務(wù)開發(fā)過程中,我們經(jīng)常會遇到往數(shù)據(jù)庫表中插入大量數(shù)據(jù)的場景,比如excel批量導(dǎo)入數(shù)據(jù)。那么該如何快速地插入數(shù)據(jù)呢?我們可以考慮使用批量插入來實現(xiàn),該方案實測每秒能達(dá)到35000條,后附具體實現(xiàn)代碼。接下來我就給大家說一下實現(xiàn)方式。
一. JDBC實現(xiàn)方案
用一個 for 循環(huán),把數(shù)據(jù)一條一條地插入;生成一條插入 sql,類似這種 insert into user(name,pwd) values('aa','123'),('cc','123')...
第一種方案,是用 for語句循環(huán)插入:
該方案的優(yōu)勢在于,JDBC 中的 PreparedStatement 有預(yù)編譯功能,預(yù)編譯之后會緩存起來。之后SQL執(zhí)行會比較快,且 JDBC可以開啟批處理,這個批處理執(zhí)行非常給力。
劣勢在于,很多時候我們的 SQL 服務(wù)器和應(yīng)用服務(wù)器可能并不是同一臺,所以必須要考慮網(wǎng)絡(luò) IO。如果網(wǎng)絡(luò) IO 比較費時間的話,那么可能會拖慢 SQL 執(zhí)行的速度。
第二種方案,是生成一條 SQL進(jìn)行插入:
該方案的優(yōu)勢在于,只有一次網(wǎng)絡(luò) IO。即使分片處理也只是數(shù)次網(wǎng)絡(luò) IO,所以這種方案不會在網(wǎng)絡(luò) IO 上花費太多時間。
當(dāng)然這種方案也有劣勢。一是 SQL 太長了,甚至可能需要分片后批量處理;二是無法充分發(fā)揮 PreparedStatement 預(yù)編譯的優(yōu)勢,SQL 要重新解析且無法復(fù)用;三是最終生成的 SQL 太長了,數(shù)據(jù)庫管理器解析這么長的 SQL 也需要時間。
我們接下來會采用第二種方案進(jìn)行實現(xiàn)。
二. 具體實現(xiàn)思路
如果我們想要拉高插入效率,肯定不能夠一條一條地插入了,必須得使用foreach批量插入;
采用多線程進(jìn)行異步插入,提升性能;
我們不可能單次提交多個insert,大量的插入操作會很耗時,短時間內(nèi)完不成,可以采用定時任務(wù)來實現(xiàn)。
接下來我們就來說說具體該怎么利用代碼進(jìn)行實現(xiàn)。
三. 代碼實現(xiàn)
本案例主要是基于SpringBoot整合mybatis進(jìn)行實現(xiàn)。
1.導(dǎo)入依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.48</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>2.創(chuàng)建啟動類
@SpringBootApplication //引導(dǎo)類核心注解
@EnableScheduling //開啟定時任務(wù)
public class BatchApplication {
public static void main(String[] args) {
SpringApplication.run(BatchApplication.class,args);
}
}3.配置文件application.yml
server:
port: 9999 # 指定端口號
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
password: 123
mybatis:
mapper-locations: classpath:mybatis/*.xml #指定mapper映射文件路徑
type-aliases-package: com.qfedu.model # 別名4.創(chuàng)建表與實體類User
創(chuàng)建表:
CREATE TABLE `user` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `username` VARCHAR(30) DEFAULT NULL, `pwd` VARCHAR(20) DEFAULT NULL, `sex` INT(11) DEFAULT NULL, `birthday` DATETIME DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8
注意:MyISAM效率會比INNODB快。
User.java
@Data
public class User {
private int id;
private String username;
private String pwd;
private int sex;
private LocalDate birthday;
}5.持久層mapper和映射文件
UserMapper.java
@Mapper
public interface UserMapper {
void insertBatch(@Param("userList") List<User> userList);
}UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qfedu.mapper.UserMapper">
<insert id="addList" parameterType="User" >
insert into user (username,pwd,sex,birthday) values
<foreach collection="list" item="item" separator=",">
(#{item.username}, #{item.pwd}, #{item.sex}, #{item.birthday})
</foreach>
</insert>
</mapper>6.開啟定時任務(wù)
SpringBoot默認(rèn)整合了scheduled,使用步驟如下:
在引導(dǎo)類加入@EnableScheduling注解,開啟定時任務(wù);
在業(yè)務(wù)層方法上加入 @Scheduled注解,定義cron表達(dá)式周期執(zhí)行。
業(yè)務(wù)層方法中開啟的線程可以根據(jù)當(dāng)前機器的配置來修改。我們這里開了7個線程,每個線程去執(zhí)行20次循環(huán),一次添加5000條數(shù)據(jù)。這里要注意mybatis批量插入時,不建議超過10000條錯誤。因為數(shù)據(jù)量過大,容易出現(xiàn)棧內(nèi)存溢出的問題。
@Component
public class UserServiceImpl {
@Autowired
private UserMapper userMapper;
@Autowired
//線程池
private ThreadPoolExecutor executor;
@Scheduled(cron = "0/20 * * * * ?") //每隔20秒執(zhí)行一次
public void addList(){
System.out.println("定時器被觸發(fā)");
long start = System.currentTimeMillis();
for (int i = 0; i < 7; i++) {
Thread thread = new Thread(() -> {
try {
for (int j = 0; j < 20; j++) {
userMapper.addList(UserUtil.getUsers(5000));
}
} catch (Exception e) {
e.printStackTrace();
}
});
try {
executor.execute(thread);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
}7.生成對象的util
我們用來模擬生成要插入的數(shù)據(jù),實際業(yè)務(wù)開發(fā)的時候可以是從excel中導(dǎo)入的數(shù)據(jù)。
public class UserUtil {
private static Random random = new Random();
public static List<User> getUsers(int num){
List<User> users = new ArrayList<>();
for (int i = 0;i<num;i++){
User user = new User();
user.setBirthday(LocalDate.now());
user.setSex(random.nextInt(2));
user.setPwd("123"+random.nextInt(100000));
user.setUsername("batch"+random.nextInt(num));
users.add(user);
}
return users;
}
}8.線程池配置
線程池參數(shù):
corePoolSize 核心線程數(shù),在線程池中要保證的最小線程數(shù);
mainumPoolSize 最大線程數(shù),線程池中能運行的最大線程數(shù);
keepAliveTime 保證存活時間,當(dāng)線程空閑時間,多久會回收線程;
unit 和keepAliveTime配合使用,時間單位;
workQueue 工作隊列,用于存儲任務(wù)在任務(wù)被執(zhí)行之前。
@Configuration
public class ThreadPoolExecutorConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor() {
//線程池中6個線程,最大8個線程,用于緩存任務(wù)的阻塞隊列數(shù)5個
ThreadPoolExecutor executor = new ThreadPoolExecutor(6, 8, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100));
executor.allowCoreThreadTimeOut(true);//允許超時
return executor;
}
}9.完整項目結(jié)構(gòu)

10.測試


總結(jié)
到此這篇關(guān)于springboot+mybatis快速插入大量數(shù)據(jù)的文章就介紹到這了,更多相關(guān)springboot mybatis快速插入大量數(shù)據(jù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java設(shè)計模式之狀態(tài)模式(State模式)介紹
這篇文章主要介紹了Java設(shè)計模式之狀態(tài)模式(State模式)介紹,本文講解了何時使用狀態(tài)模式、如何使用狀態(tài)模式等內(nèi)容,需要的朋友可以參考下2015-03-03
深入淺析SPI機制在JDK與Spring?Boot中的應(yīng)用
SPI是一種使軟件框架或庫更加模塊化、可擴展和可維護(hù)的有效方法。通過遵循“開閉原則”,?SPI?確保了系統(tǒng)的穩(wěn)定性和靈活性,從而滿足了不斷變化的業(yè)務(wù)需求,這篇文章主要介紹了SPI機制在JDK與Spring?Boot中的應(yīng)用,需要的朋友可以參考下2023-09-09
SpringBoot項目動態(tài)指定配置文件的方法示例
SpringBoot 作為 Java 開發(fā)中一個非常流行的框架,以其簡化配置和快速啟動應(yīng)用而聞名,在開發(fā)過程中,我們經(jīng)常需要根據(jù)不同的環(huán)境來加載不同的配置文件,本文給大家介紹了SpringBoot項目動態(tài)指定配置文件的方法示例,需要的朋友可以參考下2024-12-12
SpringMVC 參數(shù)綁定意義及實現(xiàn)過程解析
這篇文章主要介紹了SpringMVC 參數(shù)綁定意義及實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-11-11
解決Spring事務(wù)@Transactional多層嵌套失效問題
在使用Spring進(jìn)行事務(wù)管理時,可能會遇到事務(wù)失效的問題,主要原因包括數(shù)據(jù)庫不支持事務(wù)、方法訪問級別不是public、未被Spring管理的Bean、當(dāng)前類的方法內(nèi)部調(diào)用以及配置的事務(wù)傳播性不當(dāng)?shù)?解決事務(wù)失效的方法有使用聲明式事務(wù)處理采用合適的事務(wù)傳播行為2024-11-11

