news 2026/6/14 16:31:19

MyBatis基础入门《十二》批量操作优化:高效插入/更新万级数据,告别慢 SQL!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MyBatis基础入门《十二》批量操作优化:高效插入/更新万级数据,告别慢 SQL!

在 《MyBatis基础入门《十一》TypeHandler 详解》 中,我们打通了数据库与 Java 类型的映射通道。
但当面对导入 10 万条用户数据同步大量订单状态等场景时,逐条执行insertupdate会导致:

  • 数据库连接频繁创建/销毁
  • 事务提交次数过多
  • 网络往返延迟累积

结果:耗时几分钟甚至超时失败!

解决方案:使用MyBatis 批量操作(Batch)
本文将手把手教你实现高性能批量写入,并对比多种方案的优劣。


一、为什么普通循环插入这么慢?

// ❌ 反面教材:逐条插入(10,000 条 ≈ 10,000 次 SQL + 10,000 次网络交互) for (User user : userList) { userMapper.insert(user); // 每次都是一次独立 SQL }

性能瓶颈

  • 每次insert都是独立事务(自动提交);
  • JDBC 驱动与数据库多次通信;
  • 数据库频繁写 WAL 日志、刷盘。

💡 实测:插入 10,000 条记录,普通方式可能耗时30s+;批量方式可压至1s 内


二、方案一:SqlSession 的 Batch Executor(推荐)

MyBatis 提供了ExecutorType.BATCH模式,底层使用 JDBC 的addBatch()+executeBatch()

步骤 1:获取 Batch 模式的 SqlSession

@Test public void testBatchInsert() { // 1. 获取 BATCH 类型的 SqlSession SqlSession batchSqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); UserMapper mapper = batchSqlSession.getMapper(UserMapper.class); try { long start = System.currentTimeMillis(); // 2. 循环添加(不立即执行) for (int i = 1; i <= 10000; i++) { User user = new User(); user.setUsername("user_" + i); user.setProfile(new UserProfile("avatar.jpg", "城市" + i)); mapper.insert(user); // 仅加入批处理队列 // 3. 每 1000 条 flush 一次,防止内存溢出 if (i % 1000 == 0) { batchSqlSession.flushStatements(); // 提交当前批次 } } // 4. 提交剩余数据 batchSqlSession.commit(); long time = System.currentTimeMillis() - start; System.out.println("批量插入 10000 条耗时: " + time + " ms"); } catch (Exception e) { batchSqlSession.rollback(); throw e; } finally { batchSqlSession.close(); // 必须关闭! } }

关键点解析:

  • ExecutorType.BATCH:启用批处理模式;
  • flushStatements():手动触发executeBatch(),释放内存;
  • commit():最终提交事务;
  • 必须 close():否则资源泄漏!

✅ 优势:

  • 1 次事务提交
  • JDBC 驱动合并 SQL,减少网络往返;
  • 兼容所有数据库(MySQL、Oracle、PostgreSQL 等)。

三、方案二:XML 中使用<foreach>构建单条 INSERT(仅限 MySQL)

适用于一次性插入固定数量数据(如 100~1000 条)。

Mapper XML:

<insert id="batchInsertWithForeach"> INSERT INTO tbl_user (username, profile) VALUES <foreach collection="list" item="user" separator=","> (#{user.username}, #{user.profile, typeHandler=JsonTypeHandler}) </foreach> </insert>

调用:

userMapper.batchInsertWithForeach(userList); // 单次 SQL 插入多行

⚠️ 注意:

  • MySQL 默认max_allowed_packet限制 SQL 大小(默认 64MB);
  • 超过限制会报错,需分批调用;
  • 不支持 Oracle(语法不兼容)。

✅ 适用场景:中小批量、简单结构、MySQL 环境。


四、方案三:Spring Boot + @Transactional 批量(谨慎使用)

@Service public class UserService { @Autowired private UserMapper userMapper; @Transactional public void batchInsertInTransaction(List<User> users) { for (User user : users) { userMapper.insert(user); // 仍在同一事务中 } } }

❗ 问题:

  • 虽然事务合并了,但SQL 仍是逐条发送
  • 无 JDBC Batch 优化,性能提升有限;
  • 大数据量易导致事务日志过大、OOM

不推荐用于万级数据


五、生产环境最佳实践

✅ 1. 分批处理(防 OOM)

  • 单批次建议500~2000 条(根据字段大小调整);
  • 使用flushStatements()主动提交批次。

✅ 2. 关闭自动提交 & 合理设置事务

  • Batch 模式下,整个批次为一个事务
  • 若需部分成功,可在外层控制分段提交。

✅ 3. 数据库调优(MySQL 示例)

-- 临时关闭索引更新(插入完成后再重建) ALTER TABLE tbl_user DISABLE KEYS; -- 批量插入... -- 重建索引 ALTER TABLE tbl_user ENABLE KEYS;

或调整参数:

# my.cnf innodb_flush_log_at_trx_commit = 2 # 安全性换性能 bulk_insert_buffer_size = 256M

🔔 生产环境需 DBA 配合评估风险!

✅ 4. 监控与日志

  • 记录每批次耗时、条数;
  • 异常时记录失败数据 ID,支持重试。

六、性能对比实测(10,000 条 User)

方案耗时事务数网络交互适用场景
普通循环 insert~32,000 ms10,00010,000 次小数据量
SqlSession BATCH~800 ms11 次✅ 推荐:大数据量
<foreach>单条 INSERT~1,200 ms11 次中小批量、MySQL
Spring @Transactional 循环~28,000 ms110,000 次不推荐

💡 测试环境:MySQL 8.0, HikariCP, 16GB RAM, SSD


七、常见问题解答

❓ Q1:Batch 模式下能获取自增主键吗?

  • 不能!JDBC Batch 不支持返回生成的主键;
  • 解决方案:先批量插入无主键数据,再通过其他字段查询补全(或改用<foreach>)。

❓ Q2:如何处理部分失败?

  • MyBatis Batch 是“全有或全无”;
  • 若需部分成功,需在外层按小批次(如 100 条)循环调用,捕获异常后跳过。

❓ Q3:与 PageHelper、插件冲突吗?

  • 不冲突,但注意插件逻辑不要阻塞 Batch 执行。

八、总结

场景推荐方案
万级数据导入/同步SqlSession(BATCH)+ 分批 flush
千级以内、MySQL<foreach>单条 INSERT
需要返回主键放弃 Batch,用<foreach>或分段普通插入
高可靠性要求小批次 + 事务 + 失败重试机制

核心口诀
“大数据用 BATCH,分批 flush 防 OOM;
小批量用 foreach,主键需求要权衡!”


本文带你掌握 MyBatis 批量操作的性能优化之道,轻松应对海量数据写入挑战。
下一篇我们将深入MyBatis 与 Lombok、MapStruct 的优雅配合,打造极简 DAO 层!

👍 如果你觉得有帮助,欢迎点赞、收藏、转发!
💬 你在项目中是如何做批量处理的?欢迎评论区分享经验!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 11:46:55

51单片机TM1804控制RGB灯闪烁的问题

今天在调RGB灯带时发现&#xff1a;颜色&#xff0c;数量&#xff0c;都能正常显示 但是就是每隔一会&#xff0c;某颗RGB灯都会闪一下&#xff0c; 正常&#xff1a;异常&#xff1a;&#xff08;某个灯闪烁&#xff09;最后发现是&#xff0c;是因为中断的影响 因为51单片机没…

作者头像 李华
网站建设 2026/6/10 11:47:35

Th17 细胞的分化调控、功能特征

Th17 细胞Th17 细胞&#xff08;T helper cell 17&#xff09;是一类以分泌白介素 17&#xff08;IL-17&#xff09;为核心特征的 CD4⁺辅助性 T 细胞亚群&#xff0c;其在机体防御细胞外细菌、霉菌感染及自身免疫性疾病发生发展中具有关键作用&#xff0c;是免疫学领域的重要研…

作者头像 李华
网站建设 2026/6/12 19:26:26

Git分支管理策略优化Qwen3-VL-30B版本迭代开发流程

Git分支管理策略优化Qwen3-VL-30B版本迭代开发流程 在当前AI研发进入“大模型工业化”阶段的背景下&#xff0c;如何高效管理像Qwen3-VL-30B这样参数量高达300亿、涉及多模态融合与复杂训练流水线的旗舰级视觉语言模型&#xff0c;已成为工程团队面临的核心挑战。传统的Git工作…

作者头像 李华
网站建设 2026/6/12 19:48:02

个人或中小网站有必要做流量区分吗?

在很多站长和中小网站运营者的认知里&#xff0c;“流量区分”似乎是一件只属于大型平台的事情。动辄上亿 PV、复杂的安全体系、专业的运维团队&#xff0c;才需要去区分什么是正常流量、什么是无效流量。相比之下&#xff0c;个人博客、小型项目站、企业展示站访问量不大&…

作者头像 李华
网站建设 2026/6/12 3:16:41

10年测试架构师吐血总结,工作中用到的Linux命令都整理出来了

小李负责测试一款基于Linux系统的服务器管理软件。在测试过程中&#xff0c;他需要通过命令行界面与服务器进行交互&#xff0c;并进行各种测试和配置操作。例如&#xff0c;查看服务器上的文件结构&#xff0c;在文件中搜索特定的文本&#xff0c;检查日志文件中是否存在错误或…

作者头像 李华
网站建设 2026/6/14 4:31:08

如何在Windows上运行LobeChat镜像?WSL环境配置指南

如何在Windows上运行LobeChat镜像&#xff1f;WSL环境配置指南在当今AI应用快速普及的背景下&#xff0c;越来越多开发者希望在本地部署一个功能完整、交互流畅的聊天机器人系统。像 LobeChat 这样的开源项目提供了媲美 ChatGPT 的用户体验&#xff0c;支持多模型接入和插件扩展…

作者头像 李华