当前位置: 首页 > news >正文

【java】大数据insert的几种技术方案和优缺点

文章目录

    • 方案零:单条循环插入
    • 方案一:ExecutorType.BATCH批量提交(推荐)
      • 原理介绍
      • 代码实现
        • 开启批量会话
        • 获取Mapper,循环插入
        • Mapper XML 只需单条插入SQL
      • 优缺点分析
      • 场景建议
    • 方案二:分批控制foreach批量插入的SQL长度
      • 使用 `<foreach>` 标签拼接多值 SQL
      • foreach标签的工作原理
      • 性能瓶颈
        • SQL字符串过长
        • 数据库解析和执行慢
        • 批量过大导致JDBC驱动或数据库异常
      • 现象总结
      • 优缺点分析
      • 场景建议
    • 方案三:MyBatis-Plus的saveBatch原理与适用场景
      • 原理介绍
        • 工作机制
      • 代码实现
        • 直接调用saveBatch
        • 核心源码片段(简化说明)
      • 优缺点分析
      • 类比说明
      • 5.5 适用场景
        • 不适用场景
    • 方案四:insert into ... select ... 语法(最快)
      • 原理介绍
      • 代码实现
        • 直接在 Mapper XML 写 SQL
        • Java 端调用
        • 也可用于跨表、跨库(需支持)
      • 优缺点分析
      • 适用场景
        • 不适用场景
    • 方案五:异步队列/中间件分批入库
      • 原理介绍
      • 实现方式
        • 发送端(生产者)
        • 消费端(消费者)
        • 典型技术栈
        • 伪代码示例
      • 优缺点分析
      • 适用场景
        • 不适用场景
    • Java大数据量insert五大技术方案对比与最佳实践
      • 方案选择建议
        • 1. 业务侧批量写入(10万级以内,需事务)
        • 2. 表间大批量迁移/归档/数据清洗
        • 3. 高并发写入,需削峰填谷
        • 4. 兼容性优先、数据库多样化

在Java项目中,尤其是使用MyBatis作为ORM框架时,批量插入(batch insert)是高频需求。常见的实现方式主要有如下几种:


方案零:单条循环插入

这是最简单粗暴的方法,循环调用单条插入方法:

for (PaymentBatchDetail detail : paymentBatchDetailList) {paymentBatchDetailMapper.insert(detail);
}

这种方式虽然最简单,但每次都需要和数据库建立一次连接、执行一次SQL,效率极低,可以说是反面教材了


方案一:ExecutorType.BATCH批量提交(推荐)

原理介绍

MyBatis 支持三种执行器类型(ExecutorType):

  • SIMPLE:默认,每执行一次SQL就提交一次。
  • REUSE:复用预编译SQL(较少用)。
  • BATCH:批量执行,将多次的SQL操作先缓存起来,最后一次性提交到数据库。

ExecutorType.BATCH 的核心思想:

多条SQL先暂存在内存中,等积累到一定数量后,一次性发送给数据库批量执行。

这种方式避免了超长的拼接SQL,提升了插入效率,也降低了单条SQL的长度风险。


代码实现

开启批量会话
// 注入SqlSessionFactory
@Autowired
private SqlSessionFactory sqlSessionFactory;// 开启BATCH模式的SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
获取Mapper,循环插入
DetailMapper mapper = sqlSession.getMapper(DetailMapper.class);
int batchSize = 1000; // 每批提交1000条
List<Detail> list = ...; // 你的数据列表for (int i = 0; i < list.size(); i++) {mapper.insert(list.get(i)); // 这里调用的是单条插入的SQL// 每batchSize条提交一次if ((i + 1) % batchSize == 0 || i == list.size() - 1) {sqlSession.commit();sqlSession.clearCache();}
}
sqlSession.close();
Mapper XML 只需单条插入SQL
<insert id="insert" parameterType="com.tme.it.apply.entity.Detail">INSERT INTO payment_batch_detail (...字段...)VALUES (...#{xxx}...)
</insert>

优缺点分析

优点说明
避免超长SQL每次只执行单条SQL,数据库压力小,稳定性高。
提高性能批量提交减少了网络和数据库的交互次数,显著提升插入速度。
支持大数据量适合插入上万、甚至几十万条数据。
容易回滚某一批次出错,只需回滚该批,不影响已提交的其它批次。
缺点说明
1. 代码稍复杂需要手动管理SqlSession和批量提交逻辑。
2. 内存占用大批量时,未提交的数据会占用内存。
3. 仅适合写操作不适合复杂的读写混合业务场景。

场景建议

  • 推荐用于大数据量批量插入,如数据导入、日志归档、历史数据迁移等。
  • 批次大小建议根据服务器内存和数据库压力调整(如500~2000条/批)。
  • 需要每行进行数据加工的场景

小结
ExecutorType.BATCH 是MyBatis官方推荐的批量插入方式,性能优异,适用范围广,是解决大数据量insert的首选方案。


方案二:分批控制foreach批量插入的SQL长度

使用 <foreach> 标签拼接多值 SQL

这是最直观、最常见的批量插入写法。它通过MyBatis的 <foreach> 标签,将多条数据拼接成一条超长的 INSERT 语句。例如:

Mapper XML 示例

<!-- 批量插入付款批详情 -->
<insert id="insertBatch" parameterType="java.util.List">INSERT INTO detail (id,no,-- ... 省略其它字段 ...platform) VALUES<foreach collection="list" item="item" separator=",">(#{item.id},#{item.no},-- ... 省略其它字段 ...#{item.platform})</foreach>
</insert>

调用方式

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import com.tme.it.apply.mapper.DetailMapper;@Autowired
private DetailMapper detailMapper;// 批量插入
detailMapper.insertBatch(detailList);

分批调用

public void batchInsertByForeach(List<Detail> detailList, int batchSize) {int total = detailList.size();for (int i = 0; i < total; i += batchSize) {int end = Math.min(i + batchSize, total);List<Detail> subList = detailList.subList(i, end);detailMapper.insertBatch(subList);}
}

类比理解
你可以把这种方式理解为:

“把一沓快递单一次性交给快递员,快递员一次性录入所有单子。”
如果单子太多,快递员手忙脚乱,录入时间会变长,甚至可能出错。


foreach标签的工作原理

MyBatis 的 <foreach> 标签会将集合中的每个元素展开,拼接成一条“超长”的 SQL 语句。
比如批量插入1000条数据,最终会生成如下SQL:

INSERT INTO payment_batch_detail (col1, col2, ...)
VALUES(v11, v12, ...),(v21, v22, ...),...(v1000_1, v1000_2, ...);

性能瓶颈

SQL字符串过长
  • SQL长度限制
    数据库对单条SQL语句的长度有限制(如MySQL默认4MB),超长SQL会报错或被截断。
    但是各个数据库的可能不一致,mysql可以通过以下命令查看
SHOW VARIABLES LIKE 'max_allowed_packet';
  • 网络传输压力
    客户端与数据库之间传输超大SQL,耗时明显增加。
  • 内存消耗
    MyBatis 需要在内存中拼接和缓存整个大SQL,内存压力增大。
数据库解析和执行慢
  • SQL解析耗时
    数据库需要解析、优化、执行这条超长的SQL,解析时间随条数线性增长,甚至更快。
  • 锁表风险
    一次性插入大量数据,容易导致表锁定,影响其它业务操作。
  • 回滚压力大
    只要有一条数据出错,整个SQL都会回滚,影响批量操作的原子性和健壮性。
批量过大导致JDBC驱动或数据库异常
  • JDBC驱动本身对SQL长度和参数个数有限制,超过阈值时会抛出异常。

现象总结

  • 数据量小时,foreach批量插入性能还不错。
  • 数据量大时,SQL过长,性能急剧下降,甚至报错。
  • 这类瓶颈是MyBatis和数据库本身的机制决定的,简单调大批次反而适得其反。

小结

  • foreach适合小批量数据插入。
  • 大数据量时,必须优化批量插入方式,否则性能和稳定性都无法保证。

优缺点分析

优点说明
实现简单基于原有的foreach批量插入,只需在Java侧分批调用。
避免超长SQL每批SQL长度可控,避免超长导致的报错。
性能可接受对于中等规模(几千到几万条)数据,性能较好。
缺点说明
还是拼接SQL每批次实际上还是一条大SQL,单批过大依然有风险。涉及到mybatis的字段映射
批次过小性能下降批次太小则失去批量插入优势,批次太大又有超长风险。
事务控制如果每批都新开事务,部分批次成功可能导致数据不一致。需根据业务场景选择是否整体包事务。

场景建议

  • 适合中等批量插入(如几千~几万条)。
  • 批次大小建议结合字段数量、单条数据长度以及数据库SQL长度上限(如MySQL 4MB)来定,一般500~1000条较安全。如果insert单行的字段太多可能需要调整到10-20条
  • 对于超大批量(几十万~百万级),建议采用ExecutorType.BATCH或数据库原生导入方案。

小结
分批foreach插入兼顾了实现简易和批量性能,是老项目改造和中等数据量场景的实用方案,但不适合极大数据量。


方案三:MyBatis-Plus的saveBatch原理与适用场景

原理介绍

MyBatis-Plus 是 MyBatis 的增强工具包,极大简化了 CRUD 操作。
其中 saveBatch 方法是其批量插入的常用方案,底层实现其实是分批调用 MyBatis 的批量插入

工作机制
  • saveBatch 默认每批插入1000条(可自定义)。
  • 内部采用 ExecutorType.BATCH 的 SqlSession,循环调用单条插入方法,达到批次后统一提交。
  • 支持事务控制,遇到异常可回滚。

本质:

自动帮你分批、批量提交,封装了批处理的繁琐细节,兼顾性能与易用性。


代码实现

直接调用saveBatch
@Autowired
private DetailService detailService;List<PaymentBatchDetail> list = ...; // 待插入的数据// 默认每批1000条
detailService.saveBatch(list);// 或自定义批次大小
detailService.saveBatch(list, 500);
核心源码片段(简化说明)

MyBatis-Plus的saveBatch大致实现如下:

@Transactional(rollbackFor = Exception.class)
public boolean saveBatch(Collection<T> entityList, int batchSize) {int i = 0;for (T entity : entityList) {insert(entity); // 调用单条插入if (++i % batchSize == 0) {sqlSession.flushStatements(); // 批量提交}}sqlSession.flushStatements(); // 提交剩余的return true;
}

优缺点分析

优点说明
封装度高一行代码搞定批量插入,无需自己分批、管理事务。
性能优秀内部采用批量提交,效率媲美手写BATCH方案。
事务友好支持Spring事务,异常自动回滚。
代码简洁业务层更专注于数据处理,减少重复代码。
缺点说明
灵活性有限只支持MyBatis-Plus的实体、Service体系,无法直接自定义SQL。
批量更新/自定义SQL批处理不适用仅适合简单的批量插入、删除、更新,复杂场景需自定义。
依赖MyBatis-Plus项目未集成MP时无法使用。
伪批量实际还是循环一次次插入的,只是通过批减少了往返次数,实际效果只优于方案零。

类比说明

自动驾驶的快递车

  • 你只需要告诉快递车要送哪些包裹,车会自动帮你分批、规划路线、安全送达。

全自动厨房

  • 你只需把菜单交给厨房,后厨会自动分批烹饪、上菜,无需你操心细节。

5.5 适用场景

  • 项目已集成 MyBatis-Plus,且批量插入逻辑简单。
  • 需要快速开发、减少重复劳动。
  • 批量数据量中等到较大(几千~十几万条)。
  • 对事务、异常处理有一定要求。
不适用场景
  • 项目未使用MyBatis-Plus。
  • 需要复杂的自定义SQL或批量更新/条件插入等复杂操作。
  • 超大数据量(百万级以上)时,建议考虑数据库原生导入工具或更底层的批处理。

小结
MyBatis-Plus 的 saveBatch 是批量插入的“开箱即用”方案,极大提升了开发效率,适合大多数中大型数据批量插入场景。性能接近手写BATCH,且代码更优雅。


如需对比三种方案的适用范围和选择建议,请回复 6,我将为你总结三种方案的对比和最佳实践建议。

非常好,下面详细介绍方案四:insert into ... select ... 语法的原理与局限,包括原理、代码实现、优缺点和适用场景。


方案四:insert into … select … 语法(最快)

原理介绍

insert into ... select ... 是数据库的原生批量插入语法,直接在数据库层面实现数据的批量迁移、插入等操作。

基本语法:

INSERT INTO target_table (col1, col2, ...)
SELECT col1, col2, ...
FROM source_table
WHERE ...;

核心思想:

通过一条 SQL,从源表(或视图、子查询)中查询出数据,直接插入到目标表。
数据的读取和写入都在数据库内部完成,无需Java层循环插入,极大提升效率。


代码实现

直接在 Mapper XML 写 SQL
<insert id="insertFromSelect">INSERT INTO payment_batch_detail (col1, col2, col3)SELECT col1, col2, col3FROM temp_payment_detailWHERE batch_id = #{batchId}
</insert>
Java 端调用
detailMapper.insertFromSelect(batchId);
也可用于跨表、跨库(需支持)

优缺点分析

优点说明
性能极高纯数据库内部操作,无网络和Java层开销,适合超大批量数据迁移。
简洁高效一条SQL即可完成大批量数据插入,开发和维护成本低。
支持复杂查询可以配合JOIN、WHERE、函数等,实现复杂的数据转换和插入。
缺点说明
仅适合表间迁移只能将一张表/视图/查询结果的数据插入另一表,不能用于List<实体>等Java对象批量插入。
灵活性有限复杂业务逻辑、数据校验、转换难以在SQL层处理。
** 事务粒度大**一次性插入大量数据,若出错回滚,影响面大。
数据库压力大超大数据量时,可能导致锁表、阻塞等问题,需谨慎评估。
兼容性问题不同数据库的SQL语法、特性略有差异,跨库迁移需注意。
不适合复杂数据加工场景比如ERP、金融存在复杂字段加工的场景。

适用场景

  • 表间数据迁移、归档、历史表分表等场景。
  • 数据同步、数据分批导入(如临时表转正式表)。
  • 数据清洗、转换(可配合SQL函数、表达式)。
不适用场景
  • 需要从Java内存对象批量插入数据库。
  • 需要复杂的业务逻辑、校验、转换。
  • 跨库、跨系统的数据迁移(需数据库支持)。

小结
insert into ... select ... 是数据库原生的高效批量插入方案,适用于表间数据迁移、归档和大批量数据处理场景。
但不适合Java对象批量插入和复杂业务逻辑处理,通常作为数据迁移/归档的专项工具,在日常业务批量写入中使用有限。


方案五:异步队列/中间件分批入库

原理介绍

在高并发、大数据量写入场景下,直接同步写库容易造成数据库压力过大、响应延迟、甚至写入失败。
异步队列/中间件分批入库是一种解耦、削峰填谷的常用架构模式:

  • 写入请求先进入消息队列(如 Kafka、RabbitMQ、RocketMQ、Redis Stream 等)
  • 后端有专门的消费服务,从队列批量拉取消息,分批写入数据库
  • 实现业务与数据库写入的解耦,提升系统整体吞吐量和稳定性

实现方式

发送端(生产者)
  • 业务系统将待插入的数据(如订单、日志、明细等)异步写入消息队列,快速响应用户请求
消费端(消费者)
  • 独立的消费服务不断从队列拉取消息,聚合到一定批量后,采用批量插入方案(如MyBatis BATCH、saveBatch等)写入数据库
  • 可根据实际负载动态调整批次大小、消费速率
典型技术栈
  • 消息队列:Kafka、RabbitMQ、RocketMQ、Redis Stream、ActiveMQ等
  • 消费端:Spring Boot 定时拉取、Spring Cloud Stream、Flink、Spark Streaming等
伪代码示例
// 生产端
orderService.createOrder(orderVo) {// 业务校验// ...// 异步写入队列mqTemplate.send("order-insert-queue", orderVo);
}// 消费端批量入库
@Scheduled(fixedDelay = 1000)
public void batchInsert() {List<Order> batch = mqTemplate.receiveBatch("order-insert-queue", 500);if (!batch.isEmpty()) {orderMapper.batchInsert(batch); // 可用ExecutorType.BATCH或saveBatch}
}

优缺点分析

优点说明
** 削峰填谷,保护数据库**高峰时请求先入队,后台慢慢批量写库,避免数据库被瞬时高并发压垮。
** 高可用、解耦**业务与入库解耦,数据库故障时可暂存数据,队列可持久化。
** 支持横向扩展**消费者可多实例并发消费,提升入库能力。
** 支持批量优化**消费端可灵活采用各种批量插入方案。
缺点说明
** 架构复杂度提升**需要引入消息队列、中间件,增加运维和开发成本。
** 数据一致性挑战**队列与库之间存在延迟,极端情况下可能丢失、重复(需幂等处理)。
** 业务实时性降低**数据写入库有延迟,适合对一致性要求不高的场景。
** 监控与异常处理复杂**队列堆积、消费失败等需要完善监控和补偿机制。

适用场景

  • 高并发写入、瞬时流量高峰(如秒杀、日志收集、物联网数据采集)
  • 对写入实时性要求不高,允许秒级/分钟级延迟
  • 需要解耦业务与数据库负载,提升系统可用性与弹性
不适用场景
  • 强一致性、强实时性业务(如金融核心交易、实时扣款等)
  • 数据量很小、并发压力低的场景

小结
异步队列/中间件分批入库是一种应对高并发、大数据量写入的架构型优化,能极大提升系统的吞吐量和稳定性。适用于需要削峰填谷、解耦写入的业务场景,但对实时性和一致性有一定影响,需要配套完善的监控、幂等和补偿机制。


Java大数据量insert五大技术方案对比与最佳实践

方案编号技术方案优点缺点适用场景
1MyBatis ExecutorType.BATCH- 易用,兼容MyBatis生态
- 支持事务、回滚
- 适合中等批量
- 需手动分批,批量过大易OOM
- SQL日志不友好,调试困难
- 适合单库单表
- 业务侧批量写入
- 数据量10万级以内
- 需要事务一致性
2foreach分批拼接SQL- 控制单条SQL长度
- 适合小批量高频次
- 兼容性好
- SQL长度有限制
- 代码维护复杂
- 批量过大SQL超长
- 小批量、多次写入
- 需兼容多数据库
3MyBatis-Plus saveBatch- 封装好,易用
- 支持分批
- 代码简洁
- 底层还是分批foreach
- 不适合超大批量
- 需引入MyBatis-Plus
- 业务批量入库
- 10万级以内
- 追求开发效率
4insert into … select …- 性能极高,数据库原生
- 适合表间迁移、归档
- 支持复杂SQL
- 仅限表间/查询结果
- 业务逻辑有限
- 事务粒度大
- 表间迁移、归档
- 数据清洗、同步
- 离线批量处理
5异步队列/中间件分批入库- 削峰填谷,保护数据库
- 高可用、可扩展
- 支持批量优化
- 架构复杂
- 数据一致性、实时性挑战
- 需幂等、补偿
- 高并发写入
- 日志、订单、IoT等
- 可容忍延迟业务

方案选择建议

1. 业务侧批量写入(10万级以内,需事务)
  • 优先推荐:MyBatis ExecutorType.BATCH 或 MyBatis-Plus saveBatch
  • 理由:易用,支持事务,适合日常业务批量写入
2. 表间大批量迁移/归档/数据清洗
  • 优先推荐:insert into … select …
  • 理由:数据库原生,性能极高,适合批量迁移和数据处理
3. 高并发写入,需削峰填谷
  • 优先推荐:异步队列/中间件分批入库
  • 理由:解耦业务与数据库,提升系统可用性和吞吐量
4. 兼容性优先、数据库多样化
  • 优先推荐:foreach分批拼接SQL
  • 理由:兼容多种数据库,灵活性高,适合小批量多次插入

http://www.lryc.cn/news/611435.html

相关文章:

  • 一种基于机器学习的关键安全软件WCET分析方法概述与实际工作原理举例
  • 多传感器融合
  • 机器人权利:真实还是虚幻,机器人权利研究如何可能,道德权利与法律权利
  • nodejs 编程基础01-NPM包管理
  • 《计算机“十万个为什么”》之 面向对象 vs 面向过程:编程世界的积木与流水线
  • 【android bluetooth 协议分析 01】【HCI 层介绍 30】【hci_event和le_meta_event如何上报到btu层】
  • 零基础人工智能学习规划之路
  • 电路基础相关知识
  • HBM Basic(VCU128)
  • 翻译的本质:人工翻译vs机器翻译的核心差异与互补性
  • NumPy字符串与数学函数全解析:从基础到实战应用
  • 3. 为什么 0.1 + 0.2 != 0.3
  • ubuntu自动重启BUG排查指南
  • 前端遇到页面卡顿问题,如何排查和解决?
  • C语言:20250805学习(文件预处理)
  • 集成学习与随机森林:从原理到实践指南
  • 高通平台Wi-Fi Display学习-- 调试 Wi-Fi Display 问题
  • 【Git】实现使用SSH方式连接远程仓库时的免密操作
  • 17.8 ChatGLM3/CogVLM一键部署指南:32K长文本+多模态实战,零基础搞定企业级模型微调(附完整代码)
  • 机器学习算法系列专栏:决策树算法(初学者)
  • systemui 的启动流程是怎么样的?
  • VUE2 学习笔记 合集
  • 系统设计入门:成为更优秀的工程师
  • (ZipList入门笔记一)ZipList的节点介绍
  • 【面试场景题】日志去重与统计系统设计
  • 【STM32】HAL库中的实现(三):PWM(脉冲宽度调制)
  • 浮雕软件Artcam安装包百度云网盘下载与安装指南
  • 内部排序算法总结(考研向)
  • [验证回文串]
  • C#案例实战