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

MyBatis saveBatch 性能調(diào)優(yōu)的實(shí)現(xiàn)

 更新時間:2023年07月21日 10:09:37   作者:Nate  
本文主要介紹了MyBatis saveBatch 性能調(diào)優(yōu)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

最近在壓測一批接口,發(fā)現(xiàn)接口處理速度慢的有點(diǎn)超出預(yù)期,感覺很奇怪,后面定位發(fā)現(xiàn)是數(shù)據(jù)庫批量保存這塊很慢。 

這個項(xiàng)目用的是 mybatis-plus,批量保存直接用的是 mybatis-plus 提供的 saveBatch。 我點(diǎn)進(jìn)去看了下源碼,感覺有點(diǎn)不太對勁:

繼續(xù)追蹤了下,從這個代碼來看,確實(shí)是 for 循環(huán)一條一條執(zhí)行了 sqlSession.insert,下面的 consumer 執(zhí)行的就是上面的 sqlSession.insert:

 然后累計一定數(shù)量后,一批 flush。從這點(diǎn)來看,這個 saveBach 的性能肯定比直接一條一條 insert 快。

我直接進(jìn)行一個粗略的實(shí)驗(yàn),簡單創(chuàng)建了一張表來對比一波!

1、1000條數(shù)據(jù),一條一條插入

@Test
void MybatisPlusSaveOne() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("mybatis plus save one");
        for (int i = 0; i < 1000; i++) {
            OpenTest openTest = new OpenTest();
            openTest.setA("a" + i);
            openTest.setB("b" + i);
            openTest.setC("c" + i);
            openTest.setD("d" + i);
            openTest.setE("e" + i);
            openTest.setF("f" + i);
            openTest.setG("g" + i);
            openTest.setH("h" + i);
            openTest.setI("i" + i);
            openTest.setJ("j" + i);
            openTest.setK("k" + i);
            //一條一條插入
            openTestService.save(openTest);
        }
        sqlSession.commit();
        stopWatch.stop();
        log.info("mybatis plus save one:" + stopWatch.getTotalTimeMillis());
    } finally {
        sqlSession.close();
    }
}

3.png

可以看到,執(zhí)行一批 1000 條數(shù)的批量保存,耗費(fèi)的時間是 121011 毫秒。

2、1000條數(shù)據(jù)用 mybatis-plus 自帶的 saveBatch 插入

@Test
void MybatisPlusSaveBatch() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        List<OpenTest> openTestList = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            OpenTest openTest = new OpenTest();
            openTest.setA("a" + i);
            openTest.setB("b" + i);
            openTest.setC("c" + i);
            openTest.setD("d" + i);
            openTest.setE("e" + i);
            openTest.setF("f" + i);
            openTest.setG("g" + i);
            openTest.setH("h" + i);
            openTest.setI("i" + i);
            openTest.setJ("j" + i);
            openTest.setK("k" + i);
            openTestList.add(openTest);
        }
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("mybatis plus save batch");
        //批量插入
        openTestService.saveBatch(openTestList);
        sqlSession.commit();
        stopWatch.stop();
        log.info("mybatis plus save batch:" + stopWatch.getTotalTimeMillis());
    } finally {
        sqlSession.close();
    }
}

4.png

耗費(fèi)的時間是 59927 毫秒,比一條一條插入快了一倍,從這點(diǎn)來看,效率還是可以的。

然后常見的還有一種利用拼接 SQL 方式來實(shí)現(xiàn)批量插入,我們也來對比試試看性能如何。

3、1000 條數(shù)據(jù)用手動拼接 SQL 方式插入, 搞個手動拼接:

5.png

來跑跑下性能如何:

@Test
void MapperSaveBatch() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try {
        List<OpenTest> openTestList = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            OpenTest openTest = new OpenTest();
            openTest.setA("a" + i);
            openTest.setB("b" + i);
            openTest.setC("c" + i);
            openTest.setD("d" + i);
            openTest.setE("e" + i);
            openTest.setF("f" + i);
            openTest.setG("g" + i);
            openTest.setH("h" + i);
            openTest.setI("i" + i);
            openTest.setJ("j" + i);
            openTest.setK("k" + i);
            openTestList.add(openTest);
        }
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("mapper save batch");
        //手動拼接批量插入
        openTestMapper.saveBatch(openTestList);
        sqlSession.commit();
        stopWatch.stop();
        log.info("mapper save batch:" + stopWatch.getTotalTimeMillis());
    } finally {
        sqlSession.close();
    }
}

6.png

耗時只有 2275 毫秒,性能比 mybatis-plus 自帶的 saveBatch 好了 26 倍!

這時,我又突然回想起以前直接用 JDBC 批量保存的接口,那都到這份上了,順帶也跑跑看!

4、1000 條數(shù)據(jù)用 JDBC executeBatch 插入

@Test
void JDBCSaveBatch() throws SQLException {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    Connection connection = sqlSession.getConnection();
    connection.setAutoCommit(false);
    String sql = "insert into open_test(a,b,c,d,e,f,g,h,i,j,k) values(?,?,?,?,?,?,?,?,?,?,?)";
    PreparedStatement statement = connection.prepareStatement(sql);
    try {
        for (int i = 0; i < 1000; i++) {
            statement.setString(1,"a" + i);
            statement.setString(2,"b" + i);
            statement.setString(3, "c" + i);
            statement.setString(4,"d" + i);
            statement.setString(5,"e" + i);
            statement.setString(6,"f" + i);
            statement.setString(7,"g" + i);
            statement.setString(8,"h" + i);
            statement.setString(9,"i" + i);
            statement.setString(10,"j" + i);
            statement.setString(11,"k" + i);
            statement.addBatch();
        }
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("JDBC save batch");
        statement.executeBatch();
        connection.commit();
        stopWatch.stop();
        log.info("JDBC save batch:" + stopWatch.getTotalTimeMillis());
    } finally {
        statement.close();
        sqlSession.close();
    }
}

7.png

耗時是 55663 毫秒,所以 JDBC executeBatch 的性能跟 mybatis-plus 的 saveBatch 一樣(底層一樣)。

綜上所述,拼接 SQL 的方式實(shí)現(xiàn)批量保存效率最佳。

但是我又不太甘心,總感覺應(yīng)該有什么別的法子,然后我就繼續(xù)跟著 mybatis-plus 的源碼 debug 了一下,跟到了 MySQL 的驅(qū)動,突然發(fā)現(xiàn)有個 if 里面的條件有點(diǎn)顯眼:

8.png

就是這個叫 rewriteBatchedStatements 的玩意,從名字來看是要重寫批操作的 Statement,前面batchHasPlainStatements 已經(jīng)是 false,取反肯定是 true,所以只要這參數(shù)是 true 就會進(jìn)行一波操作。

我看了下默認(rèn)是 false。

9.png

同時我也上網(wǎng)查了下 rewriteBatchedStatements 參數(shù),好家伙,好像有用!

10.png

直接將 jdbcurl 加上了這個參數(shù):

11.png

然后繼續(xù)跑了下 mybatis-plus 自帶的 saveBatch,果然性能大大提高,跟拼接 SQL 差不多!

12.png

順帶我也跑了下 JDBC 的 executeBatch ,果然也提高了。

13.png

然后我繼續(xù) debug ,來探探 rewriteBatchedStatements 究竟是怎么 rewrite 的! 如果這個參數(shù)是 true,則會執(zhí)行下面的方法且直接返回:

14.png

看下 executeBatchedInserts 究竟干了什么:

15.png

看到上面我圈出來的代碼沒,好像已經(jīng)有點(diǎn)感覺了,繼續(xù)往下 debug。

果然!SQL 語句被 rewrite了:

image.png

對插入而言,所謂的 rewrite 其實(shí)就是將一批插入拼接成 insert into xxx values (a),(b),(c)...這樣一條語句的形式然后執(zhí)行,這樣一來跟拼接 SQL 的效果是一樣的。

那為什么默認(rèn)不給這個參數(shù)設(shè)置為 true 呢?主要有以下兩點(diǎn):

如果批量語句中的某些語句失敗,則默認(rèn)重寫會導(dǎo)致所有語句都失敗。

批量語句的某些語句參數(shù)不一樣,則默認(rèn)重寫會使得查詢緩存未命中。

看起來影響不大,所以我給我的項(xiàng)目設(shè)置上了這個參數(shù)!

最后

稍微總結(jié)下我粗略的對比(雖然粗略,但實(shí)驗(yàn)結(jié)果符合原理層面的理解),如果你想更準(zhǔn)確地做實(shí)驗(yàn),可以使用 JMH,并且測試更多組數(shù)(如 5000,10000等)的情況。

image.png

所以如果有使用 JDBC 的 Batch 性能方面的需求,要將 rewriteBatchedStatements 設(shè)置為 true,這樣能提高很多性能。

然后如果喜歡手動拼接 SQL 要注意一次拼接的數(shù)量,分批處理。

到此這篇關(guān)于MyBatis saveBatch 性能調(diào)優(yōu)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)MyBatis saveBatch 性能調(diào)優(yōu)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JVM入門之JVM內(nèi)存結(jié)構(gòu)內(nèi)容詳解

    JVM入門之JVM內(nèi)存結(jié)構(gòu)內(nèi)容詳解

    這篇文章主要介紹了JVM入門之JVM內(nèi)存結(jié)構(gòu)內(nèi)容詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • 輕松掌握J(rèn)ava代理模式

    輕松掌握J(rèn)ava代理模式

    這篇文章主要幫助大家輕松掌握J(rèn)ava代理模式,什么是靜態(tài)代理?感興趣的小伙伴們可以參考一下
    2016-09-09
  • Spring自定義MCP sse-endpoint配置指南

    Spring自定義MCP sse-endpoint配置指南

    本文給大家介紹Spring自定義MCP sse-endpoint配置指南,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2025-09-09
  • Spring Boot中的Properties的使用詳解

    Spring Boot中的Properties的使用詳解

    這篇文章主要介紹了Spring Boot中的Properties的使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • java使用Jco連接SAP過程

    java使用Jco連接SAP過程

    這篇文章主要介紹了java使用Jco連接SAP過程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • 關(guān)于Spring中@Lazy注解的使用

    關(guān)于Spring中@Lazy注解的使用

    這篇文章主要介紹了關(guān)于Spring中@Lazy注解的使用,@Lazy注解用于標(biāo)識bean是否需要延遲加載,沒加注解之前主要容器啟動就會實(shí)例化bean,本文提供了部分實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2023-08-08
  • Java如何優(yōu)雅地關(guān)閉資源try-with-resource及其異常抑制

    Java如何優(yōu)雅地關(guān)閉資源try-with-resource及其異常抑制

    這篇文章主要介紹了Java如何優(yōu)雅地關(guān)閉資源try-with-resource及其異常抑制,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-02-02
  • Java C++題解leetcode886可能的二分法并查集染色法

    Java C++題解leetcode886可能的二分法并查集染色法

    這篇文章主要為大家介紹了Java C++題解leetcode886可能的二分法并查集染色法實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • Java實(shí)現(xiàn)瀏覽器端大文件分片上傳

    Java實(shí)現(xiàn)瀏覽器端大文件分片上傳

    本文主要介紹了Java實(shí)現(xiàn)瀏覽器端大文件分片上傳,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • java中JsonObject與JsonArray轉(zhuǎn)換方法實(shí)例

    java中JsonObject與JsonArray轉(zhuǎn)換方法實(shí)例

    在項(xiàng)目日常開發(fā)中常常會遇到JSONArray和JSONObject的轉(zhuǎn)換,很多公司剛?cè)肼毜男∶刃聲ㄔ谶@里,下面這篇文章主要給大家介紹了關(guān)于java中JsonObject與JsonArray轉(zhuǎn)換方法的相關(guān)資料,需要的朋友可以參考下
    2023-04-04

最新評論

广东省| 巧家县| 玉龙| 大田县| 西盟| 元朗区| 富民县| 金川县| 江川县| 乌恰县| 耒阳市| 平顶山市| 德庆县| 十堰市| 平和县| 永和县| 洛川县| 芒康县| 遵义市| 来凤县| 嘉善县| 周至县| 琼海市| 金山区| 板桥市| 南陵县| 土默特右旗| 师宗县| 宁德市| 永德县| 寿光市| 加查县| 磐石市| 封开县| 墨玉县| 四子王旗| 綦江县| 玛纳斯县| 海丰县| 塔城市| 天津市|