Mybatis-Plus多種批量插入方案對比小結(jié)
背景
六月某日上線了一個日報(bào)表任務(wù),因是第一次上線,故需要為歷史所有日期都初始化一次報(bào)表數(shù)據(jù)
在執(zhí)行過程中發(fā)現(xiàn)新增特別的慢:插入十萬條左右的數(shù)據(jù),SQL執(zhí)行耗費(fèi)高達(dá)三分多鐘
因很早就聽聞過mybatis-plus的[偽]批量新增的問題,很快鎖定問題并進(jìn)行修復(fù),下面細(xì)節(jié)描述多種批量新增方案的具體性能表現(xiàn)
# 測試表結(jié)構(gòu) DROP TABLE IF EXISTS `biz_batch_insert_test`; CREATE TABLE `biz_batch_insert_test` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(600) DEFAULT NULL, `age` tinyint(4) unsigned DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; # name字段給的長度大了些,模擬生產(chǎn)實(shí)際表結(jié)構(gòu)占用 # 測試庫版本:5.7.5
方案一:傳統(tǒng)for循環(huán)
@Test
public void testUserInsert() {
long l = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
TestUser testUser = new TestUser();
testUser.setName("中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國");
testUser.setAge(20);
testUserService.save(testUser);
}
System.out.println("毫秒==>" + (System.currentTimeMillis() - l));
}
# 插入10萬條耗時:238040ms,大約4分鐘
方案二:使用Mybatis-Plus的saveBatch
@Test
public void testUserInsert() {
long l = System.currentTimeMillis();
List<TestUser> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
TestUser testUser = new TestUser();
testUser.setName("中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國");
testUser.setAge(20);
list.add(testUser);
}
testUserService.saveBatch(list);
System.out.println("毫秒==>" + (System.currentTimeMillis() - l));
}
# 插入耗時:62180ms,大約1分鐘
這里先留下一張saveBatch的SQL日志截圖,這里的日志是一個insert into語句,下面帶了一千條數(shù)據(jù)(MP默認(rèn)一千條一個批次),使用Mybatis Log插件查看還是單條的SQL

方案三:在方案二的基礎(chǔ)上修改MySQL連接參數(shù):rewriteBatchedStatements=true
# 與方案二測試代碼相同 # 插入耗時:35260ms,大約半分鐘
其SQL日志也如上方案二所示,MySQL Jdbc驅(qū)動在默認(rèn)情況下會無視executeBatch()語句,把我們期望批量執(zhí)行的一組sql語句拆散,一條一條地發(fā)給MySQL數(shù)據(jù)庫,直接造成較低的性能
Mysql連接配置鏈接

方案四:使用Mybatis-Plus提供的擴(kuò)展插件:InsertBatchSomeColumn
@Test
public void testUserInsert() {
long l = System.currentTimeMillis();
List<TestUser> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
TestUser testUser = new TestUser();
testUser.setName("中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國中國");
testUser.setAge(20);
list.add(testUser);
// 因?yàn)閙ysql的參數(shù)max_allowed_packet限制,所以這里程序改成單批1000條
if(list.size() == 1000){
testUserMapper.insertBatchSomeColumn(list);
list.clear();
}
}
System.out.println("毫秒==>" + (System.currentTimeMillis() - l));
}
# 插入耗時:24410ms,大約24秒
再來看看此時控制臺的SQL,與方案二的SQL有著明顯的區(qū)別,這里是將批次插入的數(shù)據(jù)拼接在同一條SQL中,對于MySQL處理來說,這是真的批量新增

配置方式
// 1. 自定義SQL注入器
public class BatchSaveSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
// 注意:保留mybatis-plus的自帶方法
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
return methodList;
}
}
// 2. 實(shí)現(xiàn)自定義baseMapper
public interface BatchSaveBaseMapper<T> extends BaseMapper<T> {
/**
* 批量插入 僅適用于mysql
*
* @param entityList
* 實(shí)體列表
* @return 影響行數(shù)
*/
Integer insertBatchSomeColumn(Collection<T> entityList);
}
// 3. 注入插件
// 方式一
@Configuration
public class MybatisPlusConfig {
/**
* 分頁插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
/**
* SQL注入器
*/
@Bean
public BatchSaveSqlInjector easySqlInjector() {
return new BatchSaveSqlInjector();
}
}
// 方式二
// 若項(xiàng)目有自定義SqlSessionFactory,也可在初始化時將自定義SQL注入器植入,參考下圖MybatisPlusAutoConfiguration.sqlSessionFactory的做法

總結(jié)
| 插入10萬數(shù)據(jù)耗時(秒) | |
|---|---|
| mybatis-plus:save | 238 |
| mybatis-plus的saveBatch:rewriteBatchedStatements=false | 62 |
| mybatis-plus的saveBatch:rewriteBatchedStatements=true | 35 |
| mybatis-plus擴(kuò)展插件:InsertBatchSomeColumn | 24 |
到此這篇關(guān)于Mybatis-Plus多種批量插入方案對比小結(jié)的文章就介紹到這了,更多相關(guān)Mybatis-Plus多種批量插入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java Spring MVC獲取請求數(shù)據(jù)詳解操作
Spring MVC 是 Spring 提供的一個基于 MVC 設(shè)計(jì)模式的輕量級 Web 開發(fā)框架,本質(zhì)上相當(dāng)于 Servlet,Spring MVC 角色劃分清晰,分工明細(xì)。由于 Spring MVC 本身就是 Spring 框架的一部分,可以說和 Spring 框架是無縫集成2021-11-11
使用Spring Boot+MyBatis框架做查詢操作的示例代碼
這篇文章主要介紹了使用Spring Boot+MyBatis框架做查詢操作的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-10-10
詳解spring項(xiàng)目中如何動態(tài)刷新bean
這篇文章主要為大家介紹了詳解spring項(xiàng)目中如何動態(tài)刷新bean,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
SpringSecurity中的UserDetails和UserDetailsService接口詳解
這篇文章主要介紹了SpringSecurity中的UserDetails和UserDetailsService接口詳解,UserDetailsService 在 Spring Security 中主要承擔(dān)查詢系統(tǒng)內(nèi)用戶、驗(yàn)證密碼、封裝用戶信息和角色權(quán)限,需要的朋友可以參考下2023-11-11
java代碼規(guī)范之不合理命名與重復(fù)代碼示例詳解
這篇文章主要為大家介紹了java代碼規(guī)范之不合理命名與重復(fù)代碼示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
Java SpringMVC數(shù)據(jù)響應(yīng)超詳細(xì)講解
Spring?MVC?是?Spring?提供的一個基于?MVC?設(shè)計(jì)模式的輕量級?Web?開發(fā)框架,本質(zhì)上相當(dāng)于?Servlet,Spring?MVC?角色劃分清晰,分工明細(xì),本章來講解SpringMVC數(shù)據(jù)響應(yīng)2022-04-04

