MyBatis批量插入的‘坑’我替你踩完了:自增ID获取、事务控制与连接池配置避坑指南

MyBatis批量插入的‘坑’我替你踩完了:自增ID获取、事务控制与连接池配置避坑指南 MyBatis批量插入实战避坑指南从自增ID获取到连接池调优批量数据插入是后端开发中的高频操作但很多开发者在本地测试通过后上线却遭遇性能滑坡。去年我们电商系统在大促时就曾因批量插入配置不当导致数据库连接池耗尽直接影响了秒杀业务。本文将分享MyBatis批量插入的五个关键陷阱及解决方案。1. 自增ID获取的时机陷阱使用ExecutorType.BATCH模式时开发者常困惑为何插入后无法立即获取自增ID。这是因为批处理模式采用了延迟执行机制——SQL语句会被缓存而非立即执行。我们做过测试在未提交事务前尝试获取ID返回的都是null。解决方案对比方案类型实现方式适用场景缺点JDBC批量KeyuseGeneratedKeysOptions需要立即获取ID部分驱动不支持事后查询通过业务字段反查无强时效要求需唯一键保证预分配ID雪花算法等分布式系统增加复杂度推荐使用JDBC驱动的批量Key返回功能配置示例如下Options(useGeneratedKeys true, keyProperty id) Insert(scriptINSERT INTO orders (...) VALUES (...) /script) void batchInsert(Param(list) ListOrder orders);注意MySQL需确保连接参数包含rewriteBatchedStatementstrue否则批量Key可能无法正常返回2. 事务控制的平衡艺术大事务是批量操作的隐形杀手。我们曾因一个事务包含5万条插入导致表锁持续15分钟。经过压测发现事务中包含超过3000条插入时MySQL的undo日志体积会指数级增长。事务拆分策略固定批次提交每1000条自动提交int batchSize 1000; for (int i 0; i total; i batchSize) { ListUser subList list.subList(i, Math.min(i batchSize, total)); userMapper.batchInsert(subList); sqlSession.commit(); // 显式提交 }定时提交模式结合Spring的Transactional注解Transactional(propagation Propagation.REQUIRES_NEW) public void batchInsertWithNewTransaction(ListUser users) { userMapper.batchInsert(users); }补偿机制记录失败点位支持断点续传3. 连接池的隐藏参数HikariCP的默认配置可能成为批量插入的性能瓶颈。某次压测中我们发现连接获取等待时间占用了总耗时的60%。关键参数调整spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 auto-commit: false # 批量操作必须关闭连接池使用禁忌避免在循环中获取/关闭连接不要混合使用Simple和Batch模式长时间批处理应设置合理的超时时间4. MySQL的优化开关rewriteBatchedStatements参数是MyBatis批量插入的性能倍增器。我们对比测试显示开启后插入1万条数据耗时从12秒降至1.8秒。其原理是将多个INSERT语句重写为单条多值语句-- 优化前 INSERT INTO t VALUES(1,1); INSERT INTO t VALUES(2,2); -- 优化后 INSERT INTO t VALUES(1,1),(2,2);完整JDBC连接字符串示例jdbc:mysql://host:3306/db?useSSLfalseuseUnicodetruecharacterEncodingUTF-8rewriteBatchedStatementstruecachePrepStmtstrueprepStmtCacheSize250prepStmtCacheSqlLimit20485. 对象映射的性能陷阱MyBatis的XML配置不当会导致批量操作变慢。我们优化过一个案例将动态SQL改为预编译后性能提升40%。优化前后对比!-- 低效写法 -- insert idbatchInsert INSERT INTO users foreach collectionlist itemitem separator, (#{item.name}, #{item.age}) /foreach /insert !-- 高效写法 -- insert idbatchInsert parameterTypejava.util.List INSERT INTO users (name, age) VALUES foreach collectionlist itemitem separator, (#{item.name}, #{item.age}) /foreach /insert对象处理建议避免在循环中调用Mapper方法使用Param明确参数类型复杂对象考虑先序列化为JSON再存储6. 监控与应急方案完善的监控能提前发现批量操作异常。我们部署的监控体系包括SQL执行时间监控超过500ms报警连接池活跃数监控事务持续时间监控应急方案模板try { batchOperation(); } catch (BatchException e) { // 1. 记录失败批次信息 log.error(Batch failed at {}, checkpoint); // 2. 尝试小批次重试 retryWithSmallerBatch(); // 3. 最终兜底方案 if (retryFailed) { saveToDeadLetterQueue(); } }在实施批量操作时建议先在预发布环境进行全量压测。我们团队总结的检查清单包括数据库备份是否完成监控告警是否就绪回滚方案是否验证流量评估是否准确