最近在開發(fā)一個后臺發(fā)送消息的功能時,由于需要給多個用戶發(fā)送消息,于是使用了 mybatis plus
提供的 saveBatch()
方法,在測試環(huán)境測試通過上預(yù)發(fā)布后,測試反應(yīng)發(fā)送消息接口很慢得等 5、6 秒,于是我就登錄線上環(huán)境查看執(zhí)行日志,發(fā)現(xiàn)是 mybatis plus
提供的 saveBatch()
方法執(zhí)行很慢導(dǎo)致,于是也就有了本篇文章。
mybatis plus 是一個流行的 ORM 框架,它基于 mybatis,提供了很多便利的功能,比如代碼生成器、通用 CRUD、分頁插件、樂觀鎖插件等。它可以讓我們更方便地操作數(shù)據(jù)庫,減少重復(fù)的代碼,提高開發(fā)效率。
案發(fā)現(xiàn)場還原
/**
*先保存通知消息,在批量保存用戶通知記錄
*/
@Transactional(rollbackFor=Exception.class)
@Override
publicbooleansaveNotice(Notifynotify,StringreceiveUserIds){
longbegin=System.currentTimeMillis();
notify.setCreateTime(newDate());
notify.setCreateBy(ShiroUtil.getSessionUid());
if(notify.getPublishTime()==null){
notify.setPublishTime(newDate());
}
booleaninsert=save(notify);
Listcollect=newArrayList<>();
ListreceiveUserList=fillNotifyRecordList(notify,receiveUserIds,collect);
notifyRecordService.saveBatch(collect);
longend=System.currentTimeMillis();
System.out.println(end-begin);
...
returninsert;
}
/**
*根據(jù)用戶id,組裝用戶通知記錄集合,返回200條記錄
*/
publicListfillNotifyRecordList(Notifynotify,StringreceiveUserIds,Listcollect) {
ListnoticeRecordList=newArrayList<>(200);
...
//組將兩百條用戶通知記錄
returnnoticeRecordList;
}
如上代碼,我有一個 saveNotice()
方法用于保存通知消息以及用戶通知記錄。執(zhí)行邏輯如下,
- 保存通知消息
- 根據(jù)用戶 id,組裝用戶通知記錄集合,返回 200 條用戶通知記錄
- 批量保存用戶通知記錄集合
前兩步驟耗時都很少,我們直接看第三步操作耗時,結(jié)合 sql 執(zhí)行日志,如下,
--slowsql5542millis.INSERTINTOoa_notify_record(notifyId,receiveUserId,receiveUserName,isRead,createTime)VALUES(?,?,?,?,?)[225,"fcd90fe3990e505d07c90a238f75e9c1","niuwawa",false,"2023-10-302304"]
5681
再結(jié)合 mybatis free log
插件打印完整 sql 如下圖,

可以看出,我們批量保存用戶通知記錄是一條記錄一條進(jìn)行保存得,已經(jīng)可以猜測就是批量插入方法導(dǎo)致得耗時較高。
這里使用得是 mybatis log free 插件,它可以自動幫我們在控制臺打印完整得 mybatis sql 語句。有需要可以在 idea 插件中心搜索 mybatis log free 下載安裝。
結(jié)合 saveBatch()
底層源碼也能夠看出,mybatis plus
對于批量操作是通過 for
循環(huán)執(zhí)行保存操作得,源碼如下圖,

到這里我們也就知道了在測試環(huán)境執(zhí)行較快得原因,因為在測試環(huán)境需要批量保存得用戶通知記錄比較少,只有幾條記錄,所以很快。但是上預(yù)發(fā)布后,由于預(yù)發(fā)布中需要批量保存得用戶通知記錄比較多達(dá)到了數(shù)百條,所以執(zhí)行較慢,耗時達(dá)到了 5、6 秒之久。
基于 Spring Boot + MyBatis Plus + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
- 項目地址:https://github.com/YunaiV/ruoyi-vue-pro
- 視頻教程:https://doc.iocoder.cn/video/
解決方法
到這里,也就是本文得重點所在了,那怎么解決這個問題嘞?如何既利用 mybatis plus
提供得便攜性,也能夠解決批量操作耗時較高得問題。
其實解決方法很簡單,只需要在 jdbcurl
上添加 rewriteBatchedStatements=true
參數(shù)即可解決這個問題。
MySQL 的 JDBC 連接的 url 中要加 rewriteBatchedStatements 參數(shù),并保證 5.1.13 以上版本的驅(qū)動,才能實現(xiàn)高性能的批量插入。
MySQL JDBC 驅(qū)動在默認(rèn)情況下會無視 executeBatch()語句,把我們期望批量執(zhí)行的一組 sql 語句拆散,一條一條地發(fā)給 MySQL 數(shù)據(jù)庫,批量插入實際上是單條插入,直接造成較低的性能。只有把 rewriteBatchedStatements 參數(shù)置為 true, 驅(qū)動才會幫你批量執(zhí)行 SQL。另外這個選項對 INSERT/UPDATE/DELETE 都有效。
rewriteBatchedStatements=true 的意思是,當(dāng)你在 Java 程序中使用批量插入/修改/刪除(batching)時,MySQL JDBC 驅(qū)動程序?qū)L試重新編寫(rewrite)你的 SQL 語句,以便更有效地執(zhí)行這些批量插入操作。
OK,在我們給 jdbcurl
上添加了參數(shù)后,看看效果,如下圖,

可以看到 jdbcurl
添加了 rewriteBatchedStatements=true
參數(shù)后,批量操作的執(zhí)行耗時已經(jīng)只有 200 毫秒,自此也就解決了 mybatis plus
提供的 saveBatch()
方法執(zhí)行耗時較高得問題。
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現(xiàn)的后臺管理系統(tǒng) + 用戶小程序,支持 RBAC 動態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能
總結(jié)
mybatis plus
給開發(fā)人員帶來了很多便利,但是其中也有一些坑點,比如上文所提到得批量操作耗時問題,如果不注意的話,就有可能調(diào)入坑里,各位開發(fā)同學(xué)可以檢查自己或者公司項目中 jdbcurl
是否缺失 rewriteBatchedStatements=true
參數(shù),加以改正,避免重復(fù)掉入這個坑里。
-
數(shù)據(jù)庫
+關(guān)注
關(guān)注
7文章
3929瀏覽量
66303 -
生成器
+關(guān)注
關(guān)注
7文章
322瀏覽量
21919 -
mybatis
+關(guān)注
關(guān)注
0文章
64瀏覽量
6936
原文標(biāo)題:Mybatis Plus很好,但也有坑!
文章出處:【微信號:芋道源碼,微信公眾號:芋道源碼】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
mybatis plus的常規(guī)用法

一篇讓你熟練掌握 MyBatis-Plus!

MyBatis-Plus的使用與測試
Fluent Mybatis、原生Mybatis和Mybatis Plus對比
MyBatis-Plus為什么不支持聯(lián)表

如何調(diào)優(yōu)MyBatis 25倍性能

MyBatis Plus如何簡化開發(fā)

評論