批量操作是数据处理场景中的性能关键点,尤其在数据迁移、报表生成等大数据量场景下,优化效果可提升10倍以上性能。以下从7个维度系统解析批量操作优化策略:
一、基础批量插入优化
1.1 常规foreach方式
<insert id="batchInsert">
INSERT INTO users(name,age) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.name}, #{user.age})
</foreach>
</insert>
优化要点:
数据分块:单次插入控制在500-1000条(避免SQL过长)
批大小控制:
// 分批次提交
int batchSize = 500;
for (int i = 0; i < total; i += batchSize) {
List<User> subList = list.subList(i, Math.min(i + batchSize, total));
mapper.batchInsert(subList);
}
1.2 批处理模式(BATCH Executor)
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
UserMapper mapper = session.getMapper(UserMapper.class);
for (User user : userList) {
mapper.insert(user);
if (counter++ % 500 == 0) {
session.flushStatements(); // 分段提交
}
}
session.commit(); // 最终提交
}
优势对比:
方式 | 10,000条耗时 | 内存占用 | 事务控制 |
---|---|---|---|
foreach | 12s | 高 | 单事务 |
BATCH模式 | 3.2s | 低 | 可分段控制 |
二、批量更新优化策略
2.1 CASE WHEN批量更新
UPDATE users SET
name = CASE id
WHEN 1 THEN '张三'
WHEN 2 THEN '李四'
END,
age = CASE id
WHEN 1 THEN 25
WHEN 2 THEN 30
END
WHERE id IN (1,2)
MyBatis动态实现:
<update id="batchUpdate">
UPDATE users
<set>
<foreach collection="list" item="user" index="index">
<if test="user.name != null">
name = CASE id
WHEN #{user.id} THEN #{user.name}
END,
</if>
age = CASE id
WHEN #{user.id} THEN #{user.age}
END
</foreach>
</set>
WHERE id IN
<foreach collection="list" item="user" open="(" separator="," close=")">
#{user.id}
</foreach>
</update>
2.2 分批次并行更新
// 使用并行流处理(注意线程安全)
List<List<User>> partitions = Lists.partition(userList, 500);
partitions.parallelStream().forEach(subList -> {
try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
UserMapper mapper = session.getMapper(UserMapper.class);
subList.forEach(mapper::update);
session.commit();
}
});
三、批量删除优化方案
3.1 使用IN语句
<delete id="batchDelete">
DELETE FROM users WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</delete>
注意:
IN条件数量限制:MySQL默认max_allowed_packet=4MB
分页删除避免锁表:
DELETE FROM users WHERE id IN (...)
LIMIT 1000; -- 分批删除
3.2 使用JOIN删除(MySQL专属)
DELETE u FROM users u
JOIN (SELECT 1 AS id UNION SELECT 2) AS tmp
ON u.id = tmp.id
MyBatis实现:
<delete id="batchDelete">
DELETE u FROM users u
JOIN (
<foreach collection="ids" item="id" separator=" UNION ALL ">
SELECT #{id} AS id
</foreach>
) tmp ON u.id = tmp.id
</delete>
四、JDBC驱动层优化
4.1 启用重写批量语句
# MySQL配置(连接参数)
rewriteBatchedStatements=true
效果对比:
参数状态 | 插入10万条耗时 | 网络请求次数 |
---|---|---|
默认(false) | 62s | 10万 |
启用(true) | 4.8s | 1 |
4.2 批处理参数优化
// 设置JDBC批处理参数
DataSource dataSource = new DataSource();
dataSource.setUrl("jdbc:mysql://host/db?useServerPrepStmts=true&cachePrepStmts=true&prepStmtCacheSize=500");
五、存储过程批量处理
5.1 Oracle数组参数示例
CREATE TYPE user_array AS TABLE OF user_obj_type;
CREATE PROCEDURE batch_insert_users(
p_users IN user_array
) AS
BEGIN
FORALL i IN 1..p_users.COUNT
INSERT INTO users VALUES p_users(i);
END;
MyBatis调用:
<select id="callBatchInsert" statementType="CALLABLE">
{call batch_insert_users(#{list,mode=IN,jdbcType=ARRAY})}
</select>
5.2 PostgreSQL批量COPY
Connection conn = sqlSession.getConnection();
CopyManager cp = new CopyManager((BaseConnection) conn);
String data = userList.stream()
.map(u -> u.getId() + "," + u.getName())
.collect(Collectors.joining("\n"));
cp.copyIn("COPY users (id,name) FROM STDIN WITH CSV", new StringReader(data));
六、事务管理策略
6.1 批量提交模式
// 每1000条提交一次
int batchSize = 1000;
for (int i = 0; i < total; i++) {
mapper.insert(list.get(i));
if (i % batchSize == 0 || i == total - 1) {
session.commit();
session.clearCache(); // 清理一级缓存
}
}
6.2 异常恢复机制
try {
// 批量操作
} catch (BatchUpdateException e) {
int[] updateCounts = e.getUpdateCounts();
for (int i = 0; i < updateCounts.length; i++) {
if (updateCounts[i] == Statement.EXECUTE_FAILED) {
log.error("第{}条数据失败: {}", i, list.get(i));
}
}
session.rollback();
throw new BusinessException("部分数据提交失败");
}
七、性能对比与选型建议
优化方案 | 适用场景 | 10万条耗时 | 内存峰值 | 实现复杂度 |
---|---|---|---|---|
foreach拼接 | 小批量简单插入 | 12s | 1.2GB | ★☆☆☆☆ |
BATCH执行器 | 标准OLTP场景 | 3.5s | 300MB | ★★☆☆☆ |
存储过程 | 超大数据量入库 | 1.8s | 150MB | ★★★★☆ |
JDBC批量协议 | MySQL/Oracle通用场景 | 2.1s | 200MB | ★★★☆☆ |
COPY机制 | PostgreSQL大数据迁移 | 0.9s | 50MB | ★★★★☆ |
选型建议:
常规业务:BATCH执行器 + 分批次提交
数据迁移:数据库专属批量协议(如MySQL的LOAD DATA)
高并发场景:结合消息队列异步处理
异构数据源:采用Spring Batch框架
八、监控与调优工具
慢SQL日志分析:
# MySQL配置
slow_query_log=1
long_query_time=1
log_output=FILE
JDBC监控指标:
// 获取批处理统计
Statement stmt = connection.createStatement();
int[] counts = stmt.executeBatch();
System.out.println("成功执行数量:" + Arrays.stream(counts).sum());
Arthas诊断:
# 监控批处理方法执行时间
watch com.example.mapper.UserMapper batchInsert '{params,returnObj}' -x 3
通过综合运用上述优化策略,可使批量操作性能得到数量级提升。建议在实际应用中根据具体数据库类型、数据规模、硬件配置等因素进行针对性调优,并建立完善的批量任务监控体系。