最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

mybatis plus saveBatch方法方法執(zhí)行慢導(dǎo)致接口發(fā)送慢解決分析

 更新時(shí)間:2023年10月31日 17:10:24   作者:wayn  
這篇文章主要為大家介紹了mybatis plus saveBatch方法導(dǎo)致接口發(fā)送慢解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

引言

作者今天在開發(fā)一個(gè)后臺(tái)發(fā)送消息的功能時(shí),由于需要給多個(gè)用戶發(fā)送消息,于是使用了 mybatis plus 提供的 saveBatch() 方法,在測(cè)試環(huán)境測(cè)試通過上預(yù)發(fā)布后,測(cè)試反應(yīng)發(fā)送消息接口很慢得等 5、6 秒,于是我就登錄預(yù)發(fā)布環(huán)境查看執(zhí)行日志,發(fā)現(xiàn)是 mybatis plus 提供的 saveBatch() 方法執(zhí)行很慢導(dǎo)致,于是也就有了本篇文章。

mybatis plus

mybatis plus 是一個(gè)流行的 ORM 框架,它基于 mybatis,提供了很多便利的功能,比如代碼生成器、通用 CRUD、分頁插件、樂觀鎖插件等。它可以讓我們更方便地操作數(shù)據(jù)庫,減少重復(fù)的代碼,提高開發(fā)效率。

注意:本文所使用的 mybatis plus 版本是 3.5.2 版本。

案發(fā)現(xiàn)場(chǎng)還原

/**
 * 先保存通知消息,在批量保存用戶通知記錄
 */
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveNotice(Notify notify, String receiveUserIds) {
    long begin = System.currentTimeMillis();
    notify.setCreateTime(new Date());
    notify.setCreateBy(ShiroUtil.getSessionUid());
    if (notify.getPublishTime() == null) {
        notify.setPublishTime(new Date());
    }
    boolean insert = save(notify);
    List<NotifyRecord> collect = new ArrayList<>();
    List<String> receiveUserList = fillNotifyRecordList(notify, receiveUserIds, collect);
    notifyRecordService.saveBatch(collect);
    long end = System.currentTimeMillis();
    System.out.println(end - begin);
    ...
    return insert;
}
/**
 * 根據(jù)用戶id,組裝用戶通知記錄集合,返回200條記錄
 */
public List<String> fillNotifyRecordList(Notify notify, String receiveUserIds, List<NotifyRecord> collect) {
    List<String> noticeRecordList = new ArrayList<>(200);
    ...
    // 組將兩百條用戶通知記錄
    return noticeRecordList;
}

如上代碼,我有一個(gè) saveNotice() 方法用于保存通知消息以及用戶通知記錄。執(zhí)行邏輯如下,

  • 保存通知消息
  • 根據(jù)用戶 id,組裝用戶通知記錄集合,返回 200 條用戶通知記錄
  • 批量保存用戶通知記錄集合

前兩步驟耗時(shí)都很少,我們直接看第三步操作耗時(shí),結(jié)合 sql 執(zhí)行日志,如下,

-- slow sql 5542 millis. INSERT INTO oa_notify_record  ( notifyId, receiveUserId, receiveUserName, isRead,  createTime )  VALUES  ( ?, ?, ?, ?,  ? )[225,"fcd90fe3990e505d07c90a238f75e9c1","niuwawa",false,"2023-10-30 23:54:04"]
5681

再結(jié)合 mybatis free log 插件打印完整 sql 如下圖,

可以看出,我們批量保存用戶通知記錄是一條一條保存得,已經(jīng)可以猜測(cè)就是批量插入方法導(dǎo)致耗時(shí)較高。

這里使用 mybatis log free 插件,它可以自動(dòng)幫我們?cè)诳刂婆_(tái)打印完整得 mybatis sql 語句。有需要可以在 idea 插件中心搜索 mybatis log free 下載安裝。

結(jié)合 saveBatch() 底層源碼也能夠看出,mybatis plus 對(duì)于批量操作是在 executeBatch() 方法內(nèi)使用 for 循環(huán)執(zhí)行插入操作得,源碼如下圖,

到這里我們應(yīng)該也能猜出了在測(cè)試環(huán)境執(zhí)行較快得原因,因?yàn)樵跍y(cè)試環(huán)境需要批量保存得用戶通知記錄比較少,只有幾條記錄,所以很快。但是上預(yù)發(fā)布后,由于預(yù)發(fā)布中需要批量保存得用戶通知記錄比較多達(dá)到了數(shù)百條,所以執(zhí)行較慢,耗時(shí)達(dá)到了 5、6 秒之久。

由上述源碼可以看出,mybatis plus 的批量操作底層使用的還是 mybatis 提供的 batch 模式實(shí)現(xiàn)批量插入以及更新的。而 mybatis 提供的 batch 模式操作底層使用的還是 jdbc 驅(qū)動(dòng)提供的批量操作模式,jdbc 批量操作示例代碼如下,

public static void main(String[] args) {
    Connection conn = null;
    PreparedStatement statement = null;
    try {
        // 數(shù)據(jù)庫連接
        String url = "jdbc:mysql://*************?autoReconnect=true&nullCatalogMeansCurrent=true&failOverReadOnly=false&useUnicode=true&characterEncoding=UTF-8";
        String user = "******";
        String password = "************";
        // 添加批處理參數(shù)
//            url = url + "&rewriteBatchedStatements=true";
        // 加載驅(qū)動(dòng)類
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 創(chuàng)建連接
        conn = DriverManager.getConnection(url, user, password);
        // 創(chuàng)建預(yù)編譯 sql 對(duì)象
        statement = conn.prepareStatement("UPDATE table_test_demo set code = ? where id = ?");
        long a = System.currentTimeMillis(); // 計(jì)時(shí)
        // 這里添加 100 個(gè)批處理參數(shù)
        for (int i = 1; i <= 100; i++) {
            statement.setString(1, "測(cè)試1");
            statement.setInt(2, i);
            statement.addBatch(); // 批量添加
        }
        long b = System.currentTimeMillis(); // 計(jì)時(shí)
        System.out.println("添加參數(shù)耗時(shí):" + (b-a)); // 計(jì)時(shí)
        int[] r = statement.executeBatch(); // 批量提交
        statement.clearBatch(); // 清空批量添加的 sql 命令列表緩存
        long c = System.currentTimeMillis(); // 計(jì)時(shí)
        System.out.println("執(zhí)行sql耗時(shí):" + (c-b)); // 計(jì)時(shí)
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 主動(dòng)釋放資源
        try {
            if (statement != null) {
                statement.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}
  • statement.addBatch() 將 sql 語句打包到一個(gè)容器中
  • statement.executeBatch() 將容器中的 sql 語句提交
  • statement.clearBatch() 清空容器,為下一次打包做準(zhǔn)備

推薦博主開源的 H5 商城項(xiàng)目waynboot-mall,這是一套全部開源的微商城項(xiàng)目,包含三個(gè)項(xiàng)目:運(yùn)營(yíng)后臺(tái)、H5 商城前臺(tái)和服務(wù)端接口。實(shí)現(xiàn)了商城所需的首頁展示、商品分類、商品詳情、商品 sku、分詞搜索、購物車、結(jié)算下單、支付寶/微信支付、收單評(píng)論以及完善的后臺(tái)管理等一系列功能。 技術(shù)上基于最新得 Springboot3.0、jdk17,整合了 MySql、Redis、RabbitMQ、ElasticSearch 等常用中間件。分模塊設(shè)計(jì)、簡(jiǎn)潔易維護(hù)

github 地址:https://github.com/wayn111/waynboot-mall

那么問題出現(xiàn)在哪里了?明明已經(jīng)使用了批量操作,但耗時(shí)還是很慢,別急,跟著我往下看。

解決方法

到這里,也就是本文得重點(diǎn)所在了,那怎么解決這個(gè)問題嘞?如何既利用 mybatis plus 提供得便攜性,也能夠解決批量操作耗時(shí)較高得問題。

雖然我們使用了 mybatis plus -> mybatis -> jdbc 這一條批量操作鏈路,但是其實(shí)我們還需要在 jdbcurl 上添加一個(gè) rewriteBatchedStatements=true 參數(shù)即可解決這個(gè)問題。

  • MySQL 的 JDBC 連接的 url 中要加 rewriteBatchedStatements 參數(shù),并保證 5.1.13 以上版本的驅(qū)動(dòng),才能實(shí)現(xiàn)高性能的批量插入。
  • MySQL JDBC 驅(qū)動(dòng)在默認(rèn)情況下會(huì)無視 executeBatch()語句,把我們期望批量執(zhí)行的一組 sql 語句拆散,一條一條地發(fā)給 MySQL 數(shù)據(jù)庫,批量插入實(shí)際上是單條插入,直接造成較低的性能。只有把 rewriteBatchedStatements 參數(shù)置為 true, 驅(qū)動(dòng)才會(huì)幫你批量執(zhí)行 SQL。另外這個(gè)選項(xiàng)對(duì) INSERT/UPDATE/DELETE 都有效。
  • rewriteBatchedStatements=true 的意思是,當(dāng)你在 Java 程序中使用批量插入/修改/刪除(batching)時(shí),MySQL JDBC 驅(qū)動(dòng)程序?qū)L試重新編寫(rewrite)你的 SQL 語句,以便更有效地執(zhí)行這些批量插入操作。

OK,在我們給 jdbcurl 上添加了參數(shù)后,看看效果,如下圖,

可以看到 jdbcurl 添加了 rewriteBatchedStatements=true 參數(shù)后,批量操作的執(zhí)行耗時(shí)已經(jīng)只有 200 毫秒,自此也就解決了 mybatis plus 提供的 saveBatch() 方法執(zhí)行耗時(shí)較高得問題。

總結(jié)

mybatis plus 給開發(fā)人員帶來了很多便利,但是其中也有一些坑點(diǎn),比如上文所提到得批量操作耗時(shí)問題,如果不注意的話,就有可能調(diào)入坑里,各位開發(fā)同學(xué)可以檢查自己或者公司項(xiàng)目中 jdbcurl 是否缺失 rewriteBatchedStatements=true 參數(shù),加以改正,避免重復(fù)掉入這個(gè)坑里。

以上就是mybatis plus saveBatch方法導(dǎo)致接口發(fā)送慢解決分析的詳細(xì)內(nèi)容,更多關(guān)于mybatis plus saveBatch接口發(fā)送的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • ElasticSearch?深度分頁示例解析

    ElasticSearch?深度分頁示例解析

    這篇文章主要為大家介紹了ElasticSearch?深度分頁示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • idea2020安裝MybatisCodeHelper插件的圖文教程

    idea2020安裝MybatisCodeHelper插件的圖文教程

    這篇文章主要介紹了idea2020安裝MybatisCodeHelper插件的方法,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 利用Socket.io 實(shí)現(xiàn)消息實(shí)時(shí)推送功能

    利用Socket.io 實(shí)現(xiàn)消息實(shí)時(shí)推送功能

    這篇文章主要介紹了利用Socket.io 實(shí)現(xiàn)消息實(shí)時(shí)推送功能,需要的朋友可以參考下
    2017-12-12
  • SpringCloud OpenFeign基本介紹與實(shí)現(xiàn)示例

    SpringCloud OpenFeign基本介紹與實(shí)現(xiàn)示例

    OpenFeign源于Netflix的Feign,是http通信的客戶端。屏蔽了網(wǎng)絡(luò)通信的細(xì)節(jié),直接面向接口的方式開發(fā),讓開發(fā)者感知不到網(wǎng)絡(luò)通信細(xì)節(jié)。所有遠(yuǎn)程調(diào)用,都像調(diào)用本地方法一樣完成
    2023-02-02
  • Quartz與Spring集成的兩種方法示例

    Quartz與Spring集成的兩種方法示例

    這篇文章主要為大家介紹了Quartz與Spring集成的兩種方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • 解析如何用兩個(gè)棧來實(shí)現(xiàn)隊(duì)列的方法

    解析如何用兩個(gè)棧來實(shí)現(xiàn)隊(duì)列的方法

    本篇文章是對(duì)如何用兩個(gè)棧實(shí)現(xiàn)隊(duì)列的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • 基于java開發(fā)之系統(tǒng)托盤的應(yīng)用

    基于java開發(fā)之系統(tǒng)托盤的應(yīng)用

    本篇文章介紹了,基于java開發(fā)之系統(tǒng)托盤的應(yīng)用。需要的朋友參考下
    2013-05-05
  • SpringBoot統(tǒng)一處理功能實(shí)現(xiàn)的全過程

    SpringBoot統(tǒng)一處理功能實(shí)現(xiàn)的全過程

    最近在做項(xiàng)目時(shí)需要對(duì)異常進(jìn)行全局統(tǒng)一處理,主要是一些分類入庫以及記錄日志等,下面這篇文章主要給大家介紹了關(guān)于SpringBoot統(tǒng)一功能處理實(shí)現(xiàn)的相關(guān)資料,文中通過圖文以及實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-03-03
  • Springboot 如何使用 SaToken 進(jìn)行登錄認(rèn)證、權(quán)限管理及路由規(guī)則接口攔截

    Springboot 如何使用 SaToken 進(jìn)行登錄認(rèn)證、權(quán)限管理及路由規(guī)則接口攔截

    Sa-Token 是一個(gè)輕量級(jí) Java 權(quán)限認(rèn)證框架,主要解決:登錄認(rèn)證、權(quán)限認(rèn)證、單點(diǎn)登錄、OAuth2.0、分布式Session會(huì)話、微服務(wù)網(wǎng)關(guān)鑒權(quán) 等一系列權(quán)限相關(guān)問題,這篇文章主要介紹了Springboot 使用 SaToken 進(jìn)行登錄認(rèn)證、權(quán)限管理以及路由規(guī)則接口攔截,需要的朋友可以參考下
    2024-06-06
  • 關(guān)于@Scheduled不執(zhí)行的原因分析

    關(guān)于@Scheduled不執(zhí)行的原因分析

    這篇文章主要介紹了關(guān)于@Scheduled不執(zhí)行的原因分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12

最新評(píng)論

安图县| 井冈山市| 页游| 含山县| 建湖县| 丰台区| 九江市| 贵溪市| 称多县| 遵化市| 海林市| 郯城县| 青州市| 枣阳市| 尼木县| 九江县| 临潭县| 永修县| 湖口县| 成都市| 望江县| 称多县| 威信县| 台东市| 大港区| 革吉县| 和平县| 长治市| 吉隆县| 秦皇岛市| 五家渠市| 瑞金市| 青浦区| 庆元县| 武宁县| 延津县| 永丰县| 宿松县| 井陉县| 大关县| 特克斯县|