java批量数据库操作需分层协同:驱动层启用rewritebatchedstatements、框架层用mybatis batch模式+手动flush、应用层分5000–10000条/批并控制线程数、数据库层调大innodb_log_file_size及关闭autocommit。

Java批量数据库操作不能只靠“拼SQL”或“开多线程”硬扛,核心在于分层协同:驱动层启用批处理、框架层用对模式、应用层控制节奏、数据库层配好参数。百万级不是单点优化的结果,而是各环节不拖后腿的系统工程。
别再无脑用 foreach 拼 VALUES
MyBatis 的 <foreach></foreach> 生成单条 INSERT 多 VALUES 是常见写法,但本质仍是“一条语句、一次网络往返、一次事务日志刷盘”。当 list 超过 1000 条,SQL 长度激增,MySQL 可能报 Packet too large;超 5000 条,解析耗时陡升,CPU 成瓶颈。它适合几百条的小批量,不是百万方案。
真正该做的是:关闭 <foreach></foreach> 批量插入入口,改用 MyBatis 的 SqlSession 批量执行器(ExecutorType.BATCH),让 JDBC 层接管批处理逻辑。
- Mapper 接口保持普通单条 insert 方法(
void insert(User user)) - Service 层手动创建 BATCH 类型 SqlSession:
sqlSessionFactory.openSession(ExecutorType.BATCH) - 循环调用 insert,每 1000 条
session.flushStatements()清空批缓冲,避免内存堆积 - 最后
session.commit()—— 此时才真正发 SQL 到数据库
JDBC 层必须开启 batch + rewriteBatchedStatements
仅靠 MyBatis BATCH 模式还不够。MySQL Connector/J 默认会把一批 PreparedStatement.executeBatch() 转成多条独立语句发送,毫无意义。关键开关是连接参数:
jdbc:mysql://host:3306/db?rewriteBatchedStatements=true&useServerPrepStmts=false&cachePrepStmts=true
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
-
rewriteBatchedStatements=true:驱动自动将多条 INSERT 合并为一条多 VALUES 的语句(类似 foreach 效果),但由驱动在客户端完成,安全可控 -
useServerPrepStmts=false:禁用服务端预编译,避免 MySQL 对大批量 prepare 的额外开销 -
cachePrepStmts=true:复用 PreparedStatement 对象,减少创建成本
实测开启后,50 万数据插入耗时可从 120 秒降至 45 秒左右。
百万级必须分批次 + 控制并发
一次性塞 100 万条进一个批处理,容易触发 JVM 内存溢出或数据库连接超时。合理做法是“双切分”:
- 数据切分:按 5000~10000 条/批,既保证批效率,又防止单批过大
- 并发切分:用线程池提交多个批次任务,但线程数 ≠ 越多越好。建议设为数据库连接池活跃连接数的 1.5 倍(如 HikariCP maxPoolSize=20,则线程池 core=15~20)
- 每个线程独占一个 SqlSession(BATCH 模式),互不干扰
- 加简单计数器或 CountDownLatch 等待全部完成,再统一 commit 或记录日志
数据库与系统级配套调优
光代码快没用,MySQL 和 OS 得跟上:
- MySQL:调大
innodb_log_file_size(建议 ≥1GB),避免频繁 checkpoint;关闭autocommit,批量期间由应用控制事务边界 - 事务大小:单个事务不超过 10 万行,否则 undo log 膨胀、锁等待加剧
- 表结构:确保主键连续、无大字段(如 TEXT)、有合适索引;导入前可临时删非必要索引,导入完重建
- JVM:堆内存不低于 2GB,推荐 G1 垃圾收集器,避免 CMS 在大批量对象分配时频繁 GC
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










