2025/12/22 16:17:38
网站建设
项目流程
重庆网站建设红旗河沟,专业建站公司设计,新闻摘抄2023年,gta5买房子网站建设在 《MyBatis基础入门《十一》TypeHandler 详解》 中#xff0c;我们打通了数据库与 Java 类型的映射通道。
但当面对 导入 10 万条用户数据、同步大量订单状态 等场景时#xff0c;逐条执行 insert 或 update 会导致#xff1a;数据库连接频繁创建/销毁事务提交次数过多网络…在 《MyBatis基础入门《十一》TypeHandler 详解》 中我们打通了数据库与 Java 类型的映射通道。但当面对导入 10 万条用户数据、同步大量订单状态等场景时逐条执行insert或update会导致数据库连接频繁创建/销毁事务提交次数过多网络往返延迟累积结果耗时几分钟甚至超时失败解决方案使用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 模式的 SqlSessionTest 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 XMLinsert idbatchInsertWithForeach INSERT INTO tbl_user (username, profile) VALUES foreach collectionlist itemuser separator, (#{user.username}, #{user.profile, typeHandlerJsonTypeHandler}) /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(ListUser 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 次中小批量、MySQLSpring Transactional 循环~28,000 ms110,000 次不推荐 测试环境MySQL 8.0, HikariCP, 16GB RAM, SSD七、常见问题解答❓ Q1Batch 模式下能获取自增主键吗不能JDBC Batch 不支持返回生成的主键解决方案先批量插入无主键数据再通过其他字段查询补全或改用foreach。❓ Q2如何处理部分失败MyBatis Batch 是“全有或全无”若需部分成功需在外层按小批次如 100 条循环调用捕获异常后跳过。❓ Q3与 PageHelper、插件冲突吗不冲突但注意插件逻辑不要阻塞 Batch 执行。八、总结场景推荐方案万级数据导入/同步SqlSession(BATCH) 分批 flush千级以内、MySQLforeach单条 INSERT需要返回主键放弃 Batch用foreach或分段普通插入高可靠性要求小批次 事务 失败重试机制✨核心口诀“大数据用 BATCH分批 flush 防 OOM小批量用 foreach主键需求要权衡”本文带你掌握 MyBatis 批量操作的性能优化之道轻松应对海量数据写入挑战。下一篇我们将深入MyBatis 与 Lombok、MapStruct 的优雅配合打造极简 DAO 层 如果你觉得有帮助欢迎点赞、收藏、转发 你在项目中是如何做批量处理的欢迎评论区分享经验