首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

MyBatis 批量插入兩大高效方案:foreach vs ExecutorType.BATCH 深度比較

     編輯:Java 相關知識

批量插入為何重要?

逐筆 INSERT 會產生大量網路往返、SQL 解析、事務提交開銷。批量插入可將 N 條語句合併為一次網路互動,效能通常提升 10x~100x。MyBatis 提供兩種主流方案:

  1. XML <foreach>:單條 SQL 多值插入
  2. ExecutorType.BATCH:JDBC 批次處理預編譯語句

方案一:XML <foreach> 多值插入

原理

生成單條 SQL:INSERT INTO t (a,b) VALUES (?,?), (?,?), ...

Mapper XML

<insert id="insertBatch" parameterType="java.util.List">
    INSERT INTO t_student (name, age)
    VALUES
    <foreach collection="list" item="stu" separator=",">
        (#{stu.name}, #{stu.age})
    </foreach>
</insert>

Java 調用

List list = ...;
mapper.insertBatch(list); // 單次呼叫

優缺點

優點缺點
單次 SQL,網路往返最少SQL 長度受限 (max_allowed_packet)
資料庫端單次解析優化超大批次可能超出 SQL 解析器限制
程式碼簡潔,無需特殊 Session 管理難以獲取自增 ID (需 useGeneratedKeys + keyProperty)
支援所有資料庫標準語法參數綁定數量極大時效能下降

適用場景

  • 批次筆數 < 1,000
  • 單條記錄較小
  • 需簡單、可移植方案

方案二:ExecutorType.BATCH 批次處理

原理

重複使用單條 PreparedStatement,累積 addBatch(),定期 executeBatch()。

Spring + MyBatis 實作

@Autowired
private SqlSessionTemplate sqlSessionTemplate;

public void batchInsert(List users) {
    SqlSession session = sqlSessionTemplate.getSqlSessionFactory()
        .openSession(ExecutorType.BATCH, false); // 手動提交
    
    try {
        UserMapper mapper = session.getMapper(UserMapper.class);
        int batchSize = 1000;
        
        for (int i = 0; i < users.size(); i++) {
            mapper.insert(users.get(i));
            
            if (i % batchSize == 0 || i == users.size() - 1) {
                session.commit();      // 刷新批次
                session.clearCache();  // 防止記憶體溢出
            }
        }
    } catch (Exception e) {
        session.rollback();
        throw e;
    } finally {
        session.close();
    }
}

通用 Mapper 寫法

public void batchInsert(String statement, List list) {
    SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
    try {
        for (int i = 0; i < list.size(); i++) {
            session.insert(statement, list.get(i));
            if (i % 1000 == 0 || i == list.size() - 1) {
                session.commit();
                session.clearCache();
            }
        }
    } finally {
        session.close();
    }
}

優缺點

優點缺點
無 SQL 長度限制,支援百萬級資料需手動管理 Session/事務/快取
JDBC 層面批次,資料庫執行計畫重用提交前無法獲取自增 ID
記憶體可控 (定期 clearCache)程式碼較繁瑣
支援更新/刪除同樣批次化若中途失敗,已提交批次無法回滾

適用場景

  • 大批次 (> 1,000,甚至百萬)
  • 需精確控制記憶體與提交頻率
  • 混合 Insert/Update/Delete 批次操作

關鍵參數調優

MySQL 驅動參數

jdbc:mysql://host:3306/db?rewriteBatchedStatements=true

開啟後,驅動會將多條 executeBatch() 重寫為多值 INSERT,雙重加速!

批次大小建議

  • 500~2,000 筆/批次 (視記錄大小調整)
  • 每批次提交後 必須 session.clearCache() 釋放一級快取

效能實測參考 (10 萬筆,單表 10 欄位)

方案耗時備註
逐筆 Insert~120 秒基準線
foreach (1000/批)~3 秒SQL 重寫開啟
BATCH (1000/批)~2.5 秒含手動提交/清快取
BATCH + rewriteBatchedStatements~1.8 秒最快

選型決策樹


批次筆數 < 1000 且 記錄輕量
    │
    ├─ 是 → XML foreach (最簡單)
    │
    └─ 否 → 需要自增 ID 即時獲取?
              │
              ├─ 是 → foreach + useGeneratedKeys (分批小量)
              │
              └─ 否 → ExecutorType.BATCH
                        │
                        ├─ MySQL + rewriteBatchedStatements=true (最快)
                        └─ 其他資料庫 / 混合操作 → BATCH 標準模式

常見坑與避坑

  1. 忘記 clearCache() → 一級快取累積導致 OOM。
  2. 自動提交開啟 → openSession(..., true) 會每次 addBatch 就提交,失去批次效果。
  3. 異常處理不當 → 已提交批次無法回滾,需業務層面補償或冪等設計。
  4. 參數對象重用 → 迴圈中 new User(),避免同一對象引用導致批次資料重複。

總結

  • 中小批次、追求簡單:XML <foreach> 首選。
  • 大批次、需記憶體控制、混合操作:ExecutorType.BATCH 必備。
  • MySQL 必加 rewriteBatchedStatements=true 發揮極致效能。
  • 兩種方案可共存:小批次用 foreach,大批次遷移到 BATCH。

根據業務量級選對方案,批量插入不再是效能瓶頸。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved