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

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

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

引言

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

mybatis plus

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

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

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

/**
 * 先保存通知消息,在批量保存用戶通知記錄
 */
@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;
}

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

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

前兩步驟耗時都很少,我們直接看第三步操作耗時,結合 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

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

可以看出,我們批量保存用戶通知記錄是一條一條保存得,已經可以猜測就是批量插入方法導致耗時較高。

這里使用 mybatis log free 插件,它可以自動幫我們在控制臺打印完整得 mybatis sql 語句。有需要可以在 idea 插件中心搜索 mybatis log free 下載安裝。

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

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

由上述源碼可以看出,mybatis plus 的批量操作底層使用的還是 mybatis 提供的 batch 模式實現(xiàn)批量插入以及更新的。而 mybatis 提供的 batch 模式操作底層使用的還是 jdbc 驅動提供的批量操作模式,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";
        // 加載驅動類
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 創(chuàng)建連接
        conn = DriverManager.getConnection(url, user, password);
        // 創(chuàng)建預編譯 sql 對象
        statement = conn.prepareStatement("UPDATE table_test_demo set code = ? where id = ?");
        long a = System.currentTimeMillis(); // 計時
        // 這里添加 100 個批處理參數(shù)
        for (int i = 1; i <= 100; i++) {
            statement.setString(1, "測試1");
            statement.setInt(2, i);
            statement.addBatch(); // 批量添加
        }
        long b = System.currentTimeMillis(); // 計時
        System.out.println("添加參數(shù)耗時:" + (b-a)); // 計時
        int[] r = statement.executeBatch(); // 批量提交
        statement.clearBatch(); // 清空批量添加的 sql 命令列表緩存
        long c = System.currentTimeMillis(); // 計時
        System.out.println("執(zhí)行sql耗時:" + (c-b)); // 計時
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 主動釋放資源
        try {
            if (statement != null) {
                statement.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}
  • statement.addBatch() 將 sql 語句打包到一個容器中
  • statement.executeBatch() 將容器中的 sql 語句提交
  • statement.clearBatch() 清空容器,為下一次打包做準備

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

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

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

解決方法

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

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

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

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

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

總結

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

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

相關文章

最新評論

丹阳市| 贡嘎县| 信阳市| 寿光市| 广河县| 梅州市| 张家口市| 南部县| 广宗县| 阜南县| 武汉市| 化隆| 舞阳县| 富民县| 九龙县| 长春市| 通州区| 额尔古纳市| 金寨县| 安泽县| 沧源| 渭源县| 桦川县| 澎湖县| 靖州| 马公市| 汉寿县| 龙里县| 柘荣县| 禹州市| 东港市| 永德县| 镇赉县| 庆阳市| 朔州市| 阳西县| 玛纳斯县| 宜州市| 石家庄市| 中江县| 甘孜县|