当前位置: 首页 > news >正文

MyBatis批量插入踩坑实录:从‘20分钟’优化到‘6秒’,我都经历了什么?

MyBatis批量插入性能优化实战:从20分钟到6秒的蜕变之旅

那天下午,系统监控突然报警,一个原本运行良好的数据导入功能竟然耗时超过20分钟。作为负责人的我,立刻意识到问题的严重性——上万条数据的批量插入操作出现了性能瓶颈。经过一系列排查和优化,最终将执行时间压缩到6秒。下面分享这段跌宕起伏的技术探索历程。

1. 问题初现:性能断崖式下跌

我们的电商促销系统需要一次性导入上万条商品数据,每条记录包含20多个字段。最初采用MyBatis默认的foreach批量插入方式:

<insert id="batchInsert" parameterType="java.util.List"> INSERT INTO product (name, price, stock, category, ..., spec_20) VALUES <foreach collection="list" item="item" separator=","> (#{item.name}, #{item.price}, ..., #{item.spec20}) </foreach> </insert>

在测试环境表现尚可,但生产环境数据量增大后,出现了以下症状:

  • 执行时间非线性增长:100条数据0.5秒,1000条15秒,10000条超过20分钟
  • 数据库监控显示:CPU和内存使用率在操作期间飙升
  • 网络抓包发现:SQL语句长度超过1MB

关键发现:当单条SQL语句过大时,数据库解析和执行效率会急剧下降

2. 第一轮优化:分批次foreach插入

基于"分而治之"的思路,首先尝试将大数据集拆分为小批次处理:

// 每批处理100条记录 int batchSize = 100; List<List<Product>> batches = ListUtils.partition(fullList, batchSize); batches.forEach(batch -> { productMapper.batchInsert(batch); });

优化效果:

  • 执行时间从20分钟降至约3分钟
  • 数据库负载明显降低

但新问题出现

  • 每次批量插入都需单独事务提交
  • 网络往返次数增加(100次 vs 原来的1次)
  • 总耗时仍不理想

3. 深入MyBatis执行机制:ExecutorType的选择

MyBatis提供三种执行器类型:

执行器类型特点适用场景
SIMPLE默认模式,每条语句单独执行常规CRUD操作
REUSE重用预处理语句相同SQL频繁执行
BATCH批量执行所有语句大批量数据操作

启用BATCH模式的代码调整:

SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); try { ProductMapper mapper = session.getMapper(ProductMapper.class); for (Product product : productList) { mapper.insert(product); } session.flushStatements(); session.commit(); } finally { session.close(); }

优化效果:

  • 执行时间从3分钟降至45秒
  • 数据库连接利用率显著提高

4. JDBC层优化:rewriteBatchedStatements参数

在数据库连接字符串中添加关键参数:

jdbc.url=jdbc:mysql://localhost:3306/db?rewriteBatchedStatements=true

这个参数的作用机制:

  • 将多个INSERT语句重写为单条多值语法
  • 减少网络传输开销
  • 允许驱动真正批量执行

结合BATCH模式的完整配置:

// 创建支持批量操作的SqlSession SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); // 获取Mapper接口 ProductMapper mapper = session.getMapper(ProductMapper.class); // 设置合适的批处理大小 int batchSize = 100; for (int i = 0; i < productList.size(); i++) { mapper.insert(productList.get(i)); if (i % batchSize == 0 || i == productList.size() - 1) { session.flushStatements(); } } session.commit();

优化效果:

  • 执行时间从45秒降至15秒
  • 内存使用更加稳定

5. 终极方案:MyBatis-Plus批量操作

对于使用MyBatis-Plus的项目,其内置的批量操作方法更加简洁高效:

// 服务层直接调用批量保存方法 productService.saveBatch(productList, 100); // 或者使用Lambda方式 boolean success = SqlHelper.executeBatch(entityClass, log, productList, 100, (sqlSession, entity) -> { sqlSession.insert("com.mapper.ProductMapper.insert", entity); });

MyBatis-Plus批量插入的核心优势:

  1. 自动事务管理:无需手动处理事务边界
  2. 智能批处理:自动按指定大小分批次提交
  3. 异常处理:完善的错误回滚机制
  4. 性能优化:内部采用最优执行策略

最终优化效果:

  • 执行时间稳定在6秒左右
  • 代码可读性和维护性大幅提升
  • 系统资源占用更加合理

6. 性能对比与选型建议

不同方案的性能测试数据(10000条记录):

方案执行时间内存峰值CPU使用率代码复杂度
原生foreach>20min
分批次foreach~3min
BATCH模式~45s
BATCH+rewrite~15s
MyBatis-Plus~6s

选型建议

  1. 中小批量数据(<1000条):简单foreach即可
  2. 大批量简单数据:BATCH模式+rewrite参数
  3. 复杂业务场景:MyBatis-Plus批量操作
  4. 超大数据量(>10万):考虑使用LOAD DATA INFILE等特殊方式

7. 避坑指南与最佳实践

在实际项目中积累的这些经验可能对你有用:

连接池配置要点

  • 适当增大最大连接数
  • 设置合理的等待超时时间
  • 为批量操作单独配置连接池
# Druid连接池示例配置 spring.datasource.druid.max-active=50 spring.datasource.druid.max-wait=60000 spring.datasource.druid.batch-max-active=10

事务管理技巧

  • 批量操作使用独立事务
  • 考虑使用编程式事务管理
  • 异常处理要确保资源释放

监控与调优

  • 记录每次批量操作的执行时间
  • 动态调整batchSize参数
  • 关注数据库的max_allowed_packet设置
// 简单的性能监控代码 long start = System.currentTimeMillis(); batchOperation.execute(); long duration = System.currentTimeMillis() - start; log.info("批量操作执行时间:{}ms,处理记录数:{}", duration, batchSize);

经过这次优化之旅,最大的体会是:性能优化需要系统化思维,从SQL语句、框架配置、JDBC参数到数据库设置,每个环节都可能成为瓶颈。而正确的监控手段和数据驱动的决策,往往比盲目尝试更有效率。

http://www.gsyq.cn/news/1485694.html

相关文章:

  • CANN矩阵乘与AllReduce融合算子
  • Maya glTF插件完整指南:3步将专业3D模型转换为Web标准格式
  • 即插即用AI记忆系统:零侵入兼容任意大模型
  • XHS-Downloader数据持久化架构深度解析:SQLite驱动的下载记录与元数据管理
  • 数字滤波器 C 语言实现大全
  • socplot足球数据可视化工具包:用Python快速画传球路线、压力热图和定制球场图
  • Kali渗透实战:从永恒之蓝漏洞到图形化桌面,手把手教你用xfreerdp连接靶机
  • 2026年甘肃旅行社推荐榜:本地人心中最靠谱的十大排名 - 资讯快报
  • 2026年6月劳力士中国区域官方售后服务体系升级优化专项核验报告 - 劳力士中国服务中心
  • Suncalc:如何轻松计算太阳和月亮位置的终极JavaScript指南
  • 如何快速上手Litematica:从安装到创建第一个Schematic
  • 宠物领养平台Java+Vue全栈项目包:含可运行源码、MySQL建库脚本与傻瓜式部署文档
  • 如何永久备份微信聊天记录?免费开源工具WeChatMsg终极解决方案
  • COLMAP三维重建完全指南:从零开始创建高质量3D模型 [特殊字符]️
  • 青岛城阳区今日黄金回收行情与六家专业服务机构全解析 - 专业黄金回收
  • 别再手动调格式了!用Overleaf写论文,搞定图片居中、段落间距与下标错误的正确姿势
  • 美团神券半价活动怎么用?不同参与方式与省钱场景详解 - 博客万
  • 避开StrongSwan 5.9.1编译安装的那些坑:配置参数详解与防火墙规则调试心得
  • 微信点餐小程序实战工程:SpringBoot后端+小程序源码+一键部署说明
  • BIO、NIO、AIO之间的区别
  • SpringBoot开发实战:从零开始构建高效微服务
  • 5分钟快速上手:开源3D CAD查看器和格式转换器的完整实战指南
  • 3种高效安装方式:Mac Mouse Fix快速部署指南
  • 垂直领域大语言模型(Vertical LLM):专业场景下的高效AI新范式
  • TradingAgents-CN:构建多智能体协作的AI金融分析平台
  • 【BBWEYY独立站规则松】2026年品牌如何用独立站建站实现从0到1的飞跃 - 比文云BBWEYY餐宝盈
  • 【信息科学与工程学】【物理/化学科学和工程技术】知识体系073——电学基础05
  • ASMREPL完全指南:从安装到寄存器操作的完整入门教程
  • 告别杂乱连线:在Altium Designer中高效绘制STM32F103C8T6与SD卡模块的原理图符号与封装
  • AndroidTDDBootStrap中的数据层设计:Retrofit与SQLBrite打造响应式数据处理