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

MyBatis中批量插入的三個關(guān)鍵優(yōu)化技巧與避坑指南

 更新時間:2026年01月31日 09:18:02   作者:皮皮林551  
這篇文章主要為大家詳細(xì)介紹了MyBatis中批量插入的三個關(guān)鍵優(yōu)化技巧與避坑指南,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下

上周接了個數(shù)據(jù)遷移的活,要把10萬條數(shù)據(jù)從老系統(tǒng)導(dǎo)入新系統(tǒng)。

寫了個簡單的批量插入,跑起來一看——5分鐘。

領(lǐng)導(dǎo)說太慢了,能不能快點?

折騰了一下午,最后優(yōu)化到3秒,記錄一下過程。

最初的代碼(5分鐘)

最開始寫的很簡單,foreach循環(huán)插入:

// 方式1:循環(huán)單條插入(最慢)
for (User user : userList) {
    userMapper.insert(user);
}

10萬條數(shù)據(jù),每條都要走一次網(wǎng)絡(luò)請求、一次SQL解析、一次事務(wù)提交。

算一下:假設(shè)每條插入需要3ms,10萬條就是300秒 = 5分鐘。

這是最蠢的寫法,但我見過很多項目都這么寫。

第一次優(yōu)化:批量SQL(30秒)

把循環(huán)插入改成批量SQL:

<!-- Mapper.xml -->
<insert id="batchInsert">
    INSERT INTO user (name, age, email) VALUES
    <foreach collection="list" item="item" separator=",">
        (#{item.name}, #{item.age}, #{item.email})
    </foreach>
</insert>
// 分批插入,每批1000條
int batchSize = 1000;
for (int i = 0; i < userList.size(); i += batchSize) {
    int end = Math.min(i + batchSize, userList.size());
    List<User> batch = userList.subList(i, end);
    userMapper.batchInsert(batch);
}

從5分鐘降到30秒,提升10倍。

原理:一條SQL插入多條數(shù)據(jù),減少網(wǎng)絡(luò)往返次數(shù)。

但還有問題:30秒還是太慢。

第二次優(yōu)化:JDBC批處理(8秒)

MySQL有個參數(shù)叫rewriteBatchedStatements,開啟后可以把多條INSERT合并成一條。

第一步:修改數(shù)據(jù)庫連接URL

jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true

第二步:使用MyBatis的批處理模式

@Autowired
private SqlSessionFactory sqlSessionFactory;

public void batchInsertWithExecutor(List<User> userList) {
    try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        
        int batchSize = 1000;
        for (int i = 0; i < userList.size(); i++) {
            mapper.insert(userList.get(i));
            
            if ((i + 1) % batchSize == 0) {
                sqlSession.flushStatements();
                sqlSession.clearCache();
            }
        }
        sqlSession.flushStatements();
        sqlSession.commit();
    }
}

從30秒降到8秒。

原理:ExecutorType.BATCH模式下,MyBatis會緩存SQL,最后一次性發(fā)送給數(shù)據(jù)庫執(zhí)行。配合rewriteBatchedStatements=true,MySQL驅(qū)動會把多條INSERT合并。

第三次優(yōu)化:多線程并行(3秒)

8秒還是不夠快,上多線程:

public void parallelBatchInsert(List<User> userList) {
    int threadCount = 4;  // 根據(jù)數(shù)據(jù)庫連接池大小調(diào)整
    int batchSize = userList.size() / threadCount;
    
    ExecutorService executor = Executors.newFixedThreadPool(threadCount);
    List<Future<?>> futures = new ArrayList<>();
    
    for (int i = 0; i < threadCount; i++) {
        int start = i * batchSize;
        int end = (i == threadCount - 1) ? userList.size() : (i + 1) * batchSize;
        List<User> subList = userList.subList(start, end);
        
        futures.add(executor.submit(() -> {
            batchInsertWithExecutor(subList);
        }));
    }
    
    // 等待所有任務(wù)完成
    for (Future<?> future : futures) {
        try {
            future.get();
        } catch (Exception e) {
            thrownew RuntimeException(e);
        }
    }
    
    executor.shutdown();
}

從8秒降到3秒。

注意事項:

  • 線程數(shù)不要超過數(shù)據(jù)庫連接池大小
  • 如果需要事務(wù)一致性,這個方案不適用
  • 要考慮主鍵沖突的問題

優(yōu)化效果對比

方案耗時提升倍數(shù)
循環(huán)單條插入300秒基準(zhǔn)
批量SQL30秒10倍
JDBC批處理8秒37倍
多線程并行3秒100倍

踩過的坑

坑1:foreach拼接SQL過長

<foreach collection="list" item="item" separator=",">

如果一次插入太多條,SQL會非常長,可能超過max_allowed_packet限制。

解決:  分批插入,每批500-1000條。

坑2:rewriteBatchedStatements不生效

檢查幾個點:

  • URL參數(shù)是否正確:rewriteBatchedStatements=true
  • 是否使用了ExecutorType.BATCH
  • MySQL驅(qū)動版本是否太舊

坑3:自增主鍵返回問題

批量插入時想獲取自增主鍵:

<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">

注意:rewriteBatchedStatements=true時,自增主鍵返回可能有問題,需要升級MySQL驅(qū)動到8.0.17+。

坑4:內(nèi)存溢出

10萬條數(shù)據(jù)一次性加載到內(nèi)存,可能OOM。

解決:分頁讀取 + 分批插入。

int pageSize = 10000;
int total = countTotal();
for (int i = 0; i < total; i += pageSize) {
    List<User> page = selectByPage(i, pageSize);
    batchInsertWithExecutor(page);
}

最終方案代碼

@Service
publicclass BatchInsertService {
    
    @Autowired
    private SqlSessionFactory sqlSessionFactory;
    
    /**
     * 高性能批量插入
     * 10萬條數(shù)據(jù)約3秒
     */
    public void highPerformanceBatchInsert(List<User> userList) {
        if (userList == null || userList.isEmpty()) {
            return;
        }
        
        int threadCount = Math.min(4, Runtime.getRuntime().availableProcessors());
        int batchSize = (int) Math.ceil((double) userList.size() / threadCount);
        
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        CountDownLatch latch = new CountDownLatch(threadCount);
        
        for (int i = 0; i < threadCount; i++) {
            int start = i * batchSize;
            int end = Math.min((i + 1) * batchSize, userList.size());
            
            if (start >= userList.size()) {
                latch.countDown();
                continue;
            }
            
            List<User> subList = new ArrayList<>(userList.subList(start, end));
            
            executor.submit(() -> {
                try {
                    doBatchInsert(subList);
                } finally {
                    latch.countDown();
                }
            });
        }
        
        try {
            latch.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        
        executor.shutdown();
    }
    
    private void doBatchInsert(List<User> userList) {
        try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            
            for (int i = 0; i < userList.size(); i++) {
                mapper.insert(userList.get(i));
                
                if ((i + 1) % 1000 == 0) {
                    sqlSession.flushStatements();
                    sqlSession.clearCache();
                }
            }
            
            sqlSession.flushStatements();
            sqlSession.commit();
        }
    }
}

總結(jié)

優(yōu)化點關(guān)鍵配置
批量SQLforeach拼接,分批1000條
JDBC批處理rewriteBatchedStatements=true+ExecutorType.BATCH
多線程線程數(shù) ≤ 連接池大小

核心原則:  減少網(wǎng)絡(luò)往返 + 減少事務(wù)次數(shù) + 并行處理。

到此這篇關(guān)于MyBatis中批量插入的三個關(guān)鍵優(yōu)化技巧與避坑指南的文章就介紹到這了,更多相關(guān)MyBatis批量插入優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于Java8實現(xiàn)提高Excel讀寫效率

    基于Java8實現(xiàn)提高Excel讀寫效率

    這篇文章主要介紹了基于Java8實現(xiàn)提高Excel讀寫效率,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-11-11
  • Spring Boot集成/輸出/日志級別控制/持久化開發(fā)實踐

    Spring Boot集成/輸出/日志級別控制/持久化開發(fā)實踐

    SpringBoot默認(rèn)集成Logback,支持靈活日志級別配置(INFO/DEBUG等),輸出包含時間戳、級別、類名等信息,并可通過Slf4j注解簡化日志記錄,實現(xiàn)控制臺與文件持久化存儲,本文給大家介紹Spring Boot集成/輸出/日志級別控制/持久化開發(fā)實踐,感興趣的朋友一起看看吧
    2025-08-08
  • 使用ResponseEntity處理API返回問題

    使用ResponseEntity處理API返回問題

    這篇文章主要介紹了使用ResponseEntity處理API返回問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • SpringBoot實現(xiàn)單點登錄的實現(xiàn)詳解

    SpringBoot實現(xiàn)單點登錄的實現(xiàn)詳解

    在現(xiàn)代的Web應(yīng)用程序中,單點登錄(Single?Sign-On)已經(jīng)變得越來越流行,在本文中,我們將使用Spring?Boot構(gòu)建一個基本的單點登錄系統(tǒng),需要的可以參考一下
    2023-05-05
  • Java性能的十一個用法分享

    Java性能的十一個用法分享

    這篇文章主要介紹了Java性能的十一個用法,需要的朋友可以參考下
    2014-10-10
  • Java實現(xiàn)Jar文件的遍歷復(fù)制與文件追加

    Java實現(xiàn)Jar文件的遍歷復(fù)制與文件追加

    這篇文章主要為大家詳細(xì)介紹了如何利用Java實現(xiàn)Jar文件的遍歷復(fù)制與文件追加功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-11-11
  • Java中淺拷貝和深拷貝詳解

    Java中淺拷貝和深拷貝詳解

    大家好,本篇文章主要講的是Java中淺拷貝和深拷貝詳解,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Spring Security其它權(quán)限校驗方式&自定義權(quán)限校驗方式

    Spring Security其它權(quán)限校驗方式&自定義權(quán)限校驗方式

    這篇文章主要介紹了Spring Security其它權(quán)限校驗方式&自定義權(quán)限校驗方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java基礎(chǔ)之?dāng)?shù)組的初始化過程

    Java基礎(chǔ)之?dāng)?shù)組的初始化過程

    Java數(shù)組分為靜態(tài)和動態(tài)初始化,靜態(tài)初始化中,程序員設(shè)定元素初始值,系統(tǒng)決定長度;動態(tài)初始化中,程序員設(shè)定長度,系統(tǒng)提供初始值,數(shù)組初始化后長度固定,存儲在堆內(nèi)存中,數(shù)組變量在棧內(nèi)存指向堆內(nèi)存數(shù)組對象,基本類型數(shù)組存儲數(shù)據(jù)值,引用類型數(shù)組存儲對象引用
    2024-10-10
  • java設(shè)計模式之組合模式(Composite)

    java設(shè)計模式之組合模式(Composite)

    這篇文章主要為大家詳細(xì)介紹了java設(shè)計模式之組合模式Composite,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-01-01

最新評論

鲁山县| 垣曲县| 星子县| 武川县| 尚志市| 金乡县| 玉门市| 房山区| 民和| 平安县| 三江| 博罗县| 桂阳县| 五华县| 和林格尔县| 聊城市| 集贤县| 陇川县| 辉南县| 凤山县| 崇明县| 宁陕县| 全椒县| 西乌| 北辰区| 石台县| 新化县| 榆树市| 武隆县| 泽库县| 大同市| 虹口区| 桓台县| 达尔| 庄河市| 永泰县| 武穴市| 隆子县| 滨海县| 天门市| 漯河市|