尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

MyBatis批量插入性能调优实战:从ExecutorType.BATCH到现代最佳实践

MyBatis批量插入性能调优实战:从ExecutorType.BATCH到现代最佳实践
📅 发布时间:2026/6/29 5:37:30

1. MyBatis批量插入性能瓶颈解析

第一次接触MyBatis批量插入是在三年前的一个电商项目,当时需要每天凌晨导入百万级商品数据。最初采用简单的单条插入方式,结果跑一次全量导入需要6个小时,数据库服务器CPU直接飙到100%。这个惨痛教训让我开始深入研究MyBatis的批量插入优化。

MyBatis默认的Simple执行器模式存在明显性能缺陷。它每次执行insert语句都会经历完整的生命周期:解析SQL→参数映射→预编译→执行→提交。当处理10万条数据时,这个流程会被重复10万次,其中预编译阶段尤其消耗资源。我做过测试,在MySQL 5.7环境下,Simple模式插入1万条记录耗时约25秒。

更糟糕的是,某些场景下还会出现"SQL语句洪水"现象。比如使用foreach拼接SQL时,如果一次性拼接5000条values,生成的SQL可能达到几MB大小。不仅网络传输耗时,数据库解析这么长的SQL也会消耗大量内存。曾经有个案例,某系统批量插入时直接把数据库连接撑爆了。

2. 经典优化方案:ExecutorType.BATCH深度剖析

ExecutorType.BATCH是我最早采用的优化方案,它的核心原理可以用"预编译复用"来概括。开启BATCH模式后,MyBatis会缓存预编译后的PreparedStatement,后续插入只需替换参数值,避免了重复预编译的开销。

具体实现需要三个关键步骤:

// 1. 创建BATCH模式的SqlSession SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); try { UserMapper mapper = session.getMapper(UserMapper.class); // 2. 循环执行插入操作 for (User user : userList) { mapper.insert(user); } // 3. 统一提交 session.flushStatements(); session.commit(); } finally { session.close(); }

这里有个容易踩的坑:忘记调用flushStatements()。有次我批量插入10万数据,内存直接OOM,就是因为没有及时清空批处理缓存。最佳实践是每1000条左右flush一次,既保证批处理效果,又避免内存溢出。

性能对比数据很能说明问题:

  • Simple模式:1万条/25秒
  • BATCH模式:1万条/3.2秒
  • foreach拼接:1万条/1.8秒(但内存消耗是BATCH的3倍)

3. 现代最佳实践:MultiRowInsertStatementProvider详解

随着MyBatis 3.5+的推出,MultiRowInsertStatementProvider成为了新的性能标杆。它通过动态SQL生成技术,在保证可读性的同时实现了接近原生JDBC的性能。我在最近一个物联网项目中采用这种方案,写入速度比传统BATCH模式又提升了40%。

具体实现示例:

try (SqlSession session = sqlSessionFactory.openSession()) { UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = generateTestUsers(10000); MultiRowInsertStatementProvider<User> insert = insertMultiple(users) .into(user) .map(id).toProperty("id") .map(username).toProperty("username") .map(password).toProperty("password") .build() .render(RenderingStrategies.MYBATIS3); mapper.insertMultiple(insert); session.commit(); }

这个方案的亮点在于:

  1. 自动优化SQL格式,生成高效的批量插入语句
  2. 内置参数绑定安全防护,避免SQL注入
  3. 支持类型处理器自动应用
  4. 与MyBatis缓存机制完美兼容

实测对比数据:

  • 传统BATCH:10万条/8.5秒
  • MultiRowInsert:10万条/5.2秒
  • JDBC原生批处理:10万条/4.9秒

4. MyBatis-Plus的saveBatch魔法

对于使用MyBatis-Plus的项目,其saveBatch方法提供了开箱即用的批量插入方案。最近帮一个初创团队优化他们的CRM系统,仅用saveBatch替换原有逻辑,数据导入时间就从2小时缩短到15分钟。

标准用法很简单:

List<User> userList = generateUsers(100000); userService.saveBatch(userList);

但有几个实用技巧值得分享:

  1. 合理设置batchSize参数,默认是1000,但根据我的测试,在SSD存储的MySQL上设置为5000更优
  2. 配合rewriteBatchedStatements=true参数使用,性能可再提升30%
  3. 事务边界要明确,建议在Service层添加@Transactional

我整理了一个性能对比矩阵:

方案10万条耗时CPU占用内存峰值
单条插入285s85%1.2GB
foreach拼接4.8s45%3.5GB
BATCH模式6.2s38%800MB
saveBatch5.5s40%1.1GB

5. 实战中的避坑指南

在金融级应用中,我们遇到过批量插入导致主从同步延迟的问题。当时采用BATCH模式每秒插入2万条记录,结果从库延迟达到20分钟。解决方案是:

  1. 在JDBC URL添加useServerPrepStmts=false
  2. 设置rewriteBatchedStatements=true
  3. 采用分片插入策略,每5000条提交一次

另一个常见问题是自增ID获取。在BATCH模式下,必须等到事务提交后才能获取真实ID。有次我们实现订单拆单功能时就踩了这个坑。解决方案有两种:

// 方案1:使用SELECT LAST_INSERT_ID()手动查询 @Options(useGeneratedKeys=true, keyProperty="id") @Insert("INSERT INTO orders(...) VALUES(...)") void insertOrder(Order order); // 方案2:采用UUID等非自增主键 public class Order { private String id = UUID.randomUUID().toString(); //... }

对于超大批量数据(千万级),建议采用分段+多线程处理。但要注意线程数不要超过数据库连接池大小,否则会适得其反。我常用的线程池配置:

ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, // 核心线程数=连接池最大连接数的一半 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy() );

6. 性能优化全链路实践

完整的性能优化应该覆盖整个数据处理链路。在最近一个日志分析系统中,我们通过全链路优化将吞吐量提升了15倍:

  1. 数据准备阶段:

    • 使用ParallelStream快速转换数据格式
    • 预分配List容量避免扩容开销
    List<Log> logs = rawData.parallelStream() .map(this::convertToLog) .collect(Collectors.toList());
  2. 数据库配置:

    spring.datasource.hikari.maximum-pool-size=20 spring.datasource.url=jdbc:mysql://...&rewriteBatchedStatements=true&cachePrepStmts=true
  3. 运行时监控:

    • 使用Micrometer记录关键指标
    • 设置合理的超时时间
    @Transactional(timeout = 30) public void batchInsert(List<Data> data) { //... }
  4. 失败处理机制:

    • 实现批量重试逻辑
    • 采用死信队列处理异常数据
    RetryTemplate retryTemplate = new RetryTemplate(); retryTemplate.execute(context -> { return batchOperation(); });

这套方案在AWS c5.2xlarge实例上实现了每秒插入12万条的稳定吞吐量,而且CPU占用保持在70%以下。关键是要根据实际业务场景调整各个阶段的参数,没有放之四海而皆准的最优解。

相关新闻

  • 大模型推理稳定性革命:透明韧性层如何实现波动归零
  • 瑞萨RA MCU LIN总线驱动开发实战:从FSP配置到代码调试全解析
  • 从docker-entrypoint.sh脚本解析容器启动时的环境变量注入与初始化流程

最新新闻

  • RA8D2 VIN模块硬件加速配置:色彩空间转换与图像缩放实战详解
  • GPT-5.6受限发布,海外AI监管升级,国产大模型迎来破局机遇?
  • 嵌入式开发硬件沙盒:RH850/U2A评估板电源、时钟与跳线配置实战
  • 云原生CI/CD:从代码提交到生产部署的“高速公路“,Tekton + ArgoCD:构建云原生DevOps流水线
  • 实时操作系统(RTOS)的核心认知基石
  • Rsysstat错误处理与日志系统:保证监控稳定性的关键

日新闻

  • ENVI5.3.1实战:基于Landsat 8影像的区域无缝镶嵌与精准裁剪
  • 3步完成HS2-HF Patch安装:新手快速打造完美HoneySelect2体验
  • 微信好友检测终极指南:3分钟发现谁已悄悄删除你

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号