作为互联网软件开发人员,相信大家都遇到过MyBatis批量插入的性能坑——数据量稍大(比如10万条),执行一次批量插入就要卡上好几分钟,不仅拖慢项目部署效率,还可能导致线上服务超时,影响用户体验。
今天就以我们团队近期的实战案例为核心,用专业视角拆解MyBatis批量插入的优化逻辑,从问题根源到落地技巧,全程干货无废话,帮你快速摆脱“批量插入卡顿”的困扰,建议收藏备用,避免后续踩坑!
10万条数据,批量插入耗时5分钟的痛点我们近期迭代一个用户数据迁移模块,需要将老系统的10万条用户历史数据,通过MyBatis批量插入到新系统的MySQL数据库中。初期开发时,程序员采用了最基础的批量插入写法,代码逻辑简单易懂,但测试时却出现了致命问题:
执行批量插入操作后,控制台一直处于阻塞状态,直到5分12秒后才提示执行成功。要知道,这个模块后续需要每日定时执行数据同步,5分钟的耗时不仅会导致同步任务堆积,还可能占用数据库连接池,影响其他核心业务的正常运行。
领导要求2天内完成优化,最终我们通过3个关键优化技巧,将批量插入耗时从5分12秒,直接缩短到3秒以内,效率提升了99%,完全满足业务需求。下面就从问题分析入手,一步步拆解优化思路。
批量插入卡顿的4个核心根源很多开发者遇到批量插入卡顿,只会盲目尝试“改代码”,却不知道卡顿的核心根源在哪。结合我们的案例,从MyBatis底层机制、SQL执行逻辑、数据库配置三个维度,拆解4个关键问题,帮你精准定位自身项目的痛点:
1. 错误使用循环单条插入,频繁建立数据库连接初期开发时,程序员误将批量插入拆分为循环单条insert语句执行,即通过for循环遍历10万条数据,每条数据都执行一次insert操作。这种写法的核心问题的是:每执行一次单条insert,都会建立一次数据库连接、执行一次SQL提交,10万条数据就会建立10万次连接。
而数据库连接的建立和释放本身就存在性能消耗,频繁的连接操作会大量占用服务器资源,导致SQL执行排队,这也是我们案例中耗时过长的最核心原因。
2. MyBatis默认配置未优化,一级缓存失效MyBatis默认开启一级缓存(SqlSession级别),但在批量插入场景中,若未对缓存配置进行优化,会导致一级缓存频繁失效,每次插入都需要重新解析SQL、绑定参数,增加额外的性能开销。
此外,MyBatis默认的ExecutorType(执行器类型)为SIMPLE,该类型适合单条SQL执行,不适合批量操作,会导致SQL执行效率低下。
3. SQL语句写法不规范,未使用批量插入语法即使避免了循环单条插入,若SQL语句写法不规范,也会导致性能瓶颈。比如未使用MySQL的VALUES批量语法(insert into table (col1,col2) values (val1,val2),(val3,val4)...),而是使用MyBatis的foreach标签拼接多条insert语句,这种写法会导致SQL语句过长,数据库解析SQL的耗时大幅增加。
4. 数据库连接池配置不合理,连接资源不足批量插入需要占用一定的数据库连接资源,若项目中数据库连接池(如Druid、HikariCP)的最大连接数配置过小,会导致批量插入操作等待连接释放,出现阻塞现象;反之,若连接数配置过大,会增加数据库的负载,同样影响执行效率。
3个关键优化技巧,从5分钟到3秒的突破结合以上问题分析,参考行业最佳实践,我们整理了3个可直接落地的优化技巧,无需复杂的架构调整,只需修改代码和配置,就能快速提升批量插入性能,完全适配大多数互联网项目的场景,建议直接照搬使用。
技巧1:摒弃循环单条插入,使用MyBatis批量插入语法(核心优化)这是最基础也是最核心的优化,核心思路是:将多条插入数据整合为一条批量SQL语句,减少数据库连接和SQL提交次数。
具体实现(以MySQL为例):
1. Mapper接口写法:使用@Insert注解,或在XML中编写批量insert语句,参数接收List集合(存储批量插入的数据)。
// Mapper接口示例public interface UserMapper { // 批量插入用户数据 int batchInsertUser(@Param("userList") List<User> userList);}2. XML映射文件写法:使用foreach标签拼接VALUES批量语法,注意foreach的collection属性需与接口参数名一致,separator设置为“,”。
<insert id="batchInsertUser" parameterType="java.util.List"> insert into t_user (id, username, phone, create_time) values <foreach collection="userList" item="user" separator=","> (#{user.id}, #{user.username}, #{user.phone}, #{user.createTime}) </foreach></insert>注意:MySQL默认允许的SQL语句长度有限制(可通过max_allowed_packet参数修改),若批量插入的数据量过大(如超过1万条),建议分批次插入(每批次1000-5000条),避免SQL语句过长导致执行失败。
技巧2:优化MyBatis配置,启用批量执行器MyBatis的默认执行器(SIMPLE)不适合批量操作,我们可以通过修改配置,启用BATCH执行器,减少SQL解析和参数绑定的开销,同时优化一级缓存配置。
具体实现(两种方式,任选其一):
1. 代码层面配置(推荐,灵活度高):在获取SqlSession时,指定执行器类型为BATCH。
// 获取批量执行的SqlSessionSqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);UserMapper userMapper = sqlSession.getMapper(UserMapper.class);// 执行批量插入userMapper.batchInsertUser(userList);// 手动提交事务(批量执行需手动提交)sqlSession.commit();// 关闭SqlSessionsqlSession.close();2. 全局配置层面(不推荐,影响所有SQL执行):在mybatis-config.xml中配置默认执行器类型为BATCH。
<settings> <!-- 启用批量执行器 --> <setting name="defaultExecutorType" value="BATCH"/> <!-- 优化一级缓存,避免频繁失效 --> <setting name="localCacheScope" value="SESSION"/></settings>技巧3:优化数据库连接池和MySQL配置,释放底层性能代码和MyBatis配置优化完成后,还需要优化数据库层面的配置,避免因底层资源限制,导致优化效果打折扣,重点优化两个方面:
1. 数据库连接池配置(以Druid为例):根据服务器性能和数据量,合理设置最大连接数、最小空闲连接数,避免连接不足或连接过多。
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: # 最大连接数(推荐设置为CPU核心数*2 + 1) maxActive: 17 # 最小空闲连接数 minIdle: 5 # 连接超时时间(毫秒) maxWait: 60000 # 关闭空闲连接的超时时间(毫秒) minEvictableIdleTimeMillis: 3000002. MySQL配置优化(修改my.cnf或my.ini文件):优化与批量插入相关的参数,提升数据库写入性能。
# 增大SQL语句最大长度(根据实际需求调整)max_allowed_packet = 100M# 关闭自动提交(批量插入时手动提交,提升效率)autocommit = 0# 优化写入缓存,提升批量写入速度innodb_buffer_pool_size = 2G # 建议设置为服务器内存的50%-70%innodb_flush_log_at_trx_commit = 2优化完成后,我们再次测试10万条数据批量插入:分20批次(每批次5000条),启用BATCH执行器,修改连接池和MySQL配置,最终执行耗时2.8秒,完全达到业务要求。
你在MyBatis批量插入中,踩过哪些坑?以上就是我们团队从实战中总结的MyBatis批量插入优化技巧,从5分钟到3秒的突破,核心就是“找对问题根源、针对性优化”——摒弃循环单条插入、启用批量执行器、优化数据库配置,这3步就能解决80%的批量插入性能问题。
但实际开发中,不同项目的场景不同(比如数据量、数据库类型、服务器性能不同),优化效果也会有所差异。
在这里想问问屏幕前的各位开发者:你在使用MyBatis批量插入时,遇到过哪些难以解决的性能坑?又是如何优化的?评论区分享你的实战经验,我们一起交流学习,避坑提速!
最后,觉得这篇干货有用的话,记得点赞+收藏+转发,关注我,后续分享更多MyBatis实战优化技巧和软件开发干货~
转载请注明来自海坡下载,本文标题:《mybatis的优化(MyBatis批量插入优化从5分钟到3秒)》
京公网安备11000000000001号
京ICP备11000001号
还没有评论,来说两句吧...