记录一次 MyBatis 批量插入的优化-BatchInsert
记录在一次项目问题排查过程中,遇到在数据量大的情况下,向数据库批量插入非常耗时长的问题。
1、分析
首先,代码是在 service 中,采用的是 for 循环调用 insert 语句的方式:
for(int i =0; i < list.size(); i++) {
baseMapper.insert(list.get(i));
}
此代码的实际执行 sql 就是一个个 insert 语句

2、优化过程
在 Mysql Docs 中,提到过这种情况,如果优化插入速度,可以将多个小型操作组合到一个大型操作中。
就是在 service 层只调用一次,在 mapper 中进行循环
mapper 中
<insert id="batchInsert" parameterType="java.util.List">
insert into USER (id, name) values
<foreach collection="list" item="model" index="index" separator=",">
(#{model.id}, #{model.name})
</foreach>
</insert>
这样执行,相当于在单个连接中,执行一个 insert 语句,在一定程度上有很好的优化效果。

但是此此操作依然存在限制。经过项目实践,当表的列数比较多(20+),以及一次性插入的行数较多(5000+)时,整个插入的耗时十分漫长,达到了14分钟。
查阅资料可以发现
Insert inside Mybatis foreach is not batch, this is a single (could become giant) SQL statement and that brings drawbacks:
- some database such as Oracle here does not support.
- in relevant cases: there will be a large number of records to insert and the database configured limit (by default around 2000 parameters per statement) will be hit, and eventually possibly DB stack error if the statement itself become too large.
Iteration over the collection must not be done in the mybatis XML. Just execute a simple Insertstatement in a Java Foreach loop. The most important thing is the session Executor type.

Unlike default ExecutorType.SIMPLE, the statement will be prepared once and executed for each record to insert.
从资料中可知,默认执行器类型为Simple,会为每个语句创建一个新的预处理语句,也就是创建一个PreparedStatement对象。
在我们的项目中,会不停地使用批量插入这个方法,而因为MyBatis对于含有
<foreach>的语句,无法采用缓存,那么在每次调用方法时,都会重新解析sql语句。
Internally, it still generates the same single insert statement with many placeholders as the JDBC code above.
MyBatis has an ability to cache PreparedStatement, but this statement cannot be cached because it contains
<foreach />element and the statement varies depending on the parameters. As a result, MyBatis has to 1) evaluate the foreach part and 2) parse the statement string to build parameter mapping [1] on every execution of this statement.And these steps are relatively costly process when the statement string is big and contains many placeholders.
[1] simply put, it is a mapping between placeholders and the parameters.
从上述资料可知,耗时就耗在,由于我foreach后有5000+个values,所以这个PreparedStatement特别长,包含了很多占位符,对于占位符和参数的映射尤其耗时。并且,查阅相关资料可知,values的增长与所需的解析时间,是呈指数型增长的。

- 所以如果使用 foreach 的方式插入,可以将数据进行分页,分批插入,一次插入20-50条数据。
而在 MyBatis 官网,是有另一种优化方案的,可以参考地址 http://www.mybatis.org/mybatis-dynamic-sql/docs/insert.html 中 Batch Insert Support 标题里的内容

即基本思想是将 MyBatis session 的 executor type 设为 Batch ,然后多次执行插入语句。就类似于JDBC的下面语句一样。

3、总结
经过试验,使用了 ExecutorType.BATCH 的插入方式,性能显著提升,不到 2s 便能全部插入完成。
总结一下,如果MyBatis需要进行批量插入,推荐使用 ExecutorType.BATCH 的插入方式,如果非要使用 的插入的话,需要将每次插入的记录控制在 20~50 左右。
记录一次 MyBatis 批量插入的优化-BatchInsert的更多相关文章
- 160421、MyBatis批量插入数据
在程序中封装了一个List集合对象,然后需要把该集合中的实体插入到数据库中,由于项目使用了Spring+MyBatis的配置,所以打算使用MyBatis批量插入,由于之前没用过批量插入,在网上找了一些 ...
- oracle+mybatis批量插入踩坑记
最近在项目中需要使用oracle+mybatis批量插入数据,因为自增主键,遇到问题,现记录如下: 一.常用的两种sql写法报错 1.insert ... values ... <insert ...
- mybatis批量插入数据到oracle
mybatis 批量插入数据到oracle报 ”java.sql.SQLException: ORA-00933: SQL 命令未正确结束“ 错误解决方法 oracle批量插入使用 insert a ...
- springMVC 接收数组参数,mybatis 接收数组参数,mybatis批量插入/批量删除案例
案例是给一个用户赋予多个权限,多个权限用其对应的主键 id 为参数,组成了 一个id数组,传给springMVC,然后springMVC传给mybatis,然后mybatis批量插入.其实类似的场景还 ...
- 解决Oracle+Mybatis批量插入报错:SQL 命令未正确结束
Mybatis批量插入需要foreach元素.foreach元素有以下主要属性: (1)item:集合中每一个元素进行迭代时的别名. (2)index:指定一个名字,用于表示在迭代过程中,每次迭代到的 ...
- JDBC批量插入数据优化,使用addBatch和executeBatch
JDBC批量插入数据优化,使用addBatch和executeBatch SQL的批量插入的问题,如果来个for循环,执行上万次,肯定会很慢,那么,如何去优化呢? 解决方案:用 preparedSta ...
- MyBatis批量插入数据(MySql)
由于项目需要生成多条数据,并保存到数据库当中,在程序中封装了一个List集合对象,然后需要把该集合中的实体插入到数据库中,项目使用了Spring+MyBatis,所以打算使用MyBatis批量插入,应 ...
- mybatis批量插入oracle时报错:unique constraint (table name) violated
mybatis批量插入oracle时报错:unique constraint (table name) violated,是因为插入的集合中有两条相同唯一约束的数据.
- Mybatis 批量插入数据
--mybatis 批量插入数据 --1.Oracle(需要测试下是否支持MySQL) < insert id ="insertBatch" parameterType=&q ...
- 【mybatis批量插入】
mybatis批量插入操作: MySQL:1.INSERT INTO TABLE_NAME(ID,NAME)VALUES(1,'张三'),(2,'李四') 2.INS ...
随机推荐
- 5.Ceph 基础篇 - 认证
文章转载自:https://mp.weixin.qq.com/s?__biz=MzI1MDgwNzQ1MQ==&mid=2247485272&idx=1&sn=4b27c357 ...
- haproxy + keeplived
两台主机: 192.168.2.163 192.168.2.165 # yum安装haproxy yum install haproxy # cat /etc/haproxy/haproxy.cfg ...
- Elasticsearch:如何实现对 emoji 表情符号进行搜索
转摘自:https://elasticstack.blog.csdn.net/article/details/114261636 Elasticsearch 是一个应用非常广泛的搜索引擎.它可以对文字 ...
- 延申三大问题中的第三个问题处理---发布更新时先把服务从注册中心给down下来,等待一段时间后再能更新模块
一开始采取的思路大致如下: 在preStop中使用/bin/sh命令,先down 然后sleep一段时间, 这种思路的执行情况如下: 假若升级容器使用的镜像版本的话,先执行preStop中的命令,sl ...
- 8Hello world
Name=input("请输入你的名字:") print('欢迎你',Name)
- python及第三方库交叉编译
一.前言: 网上关于python的交叉编译的文章很多,但是关于python第三库的交叉编译的文章就比较少了,而且很多标题是第三方库的交叉编译,但是实际上用到的都是不需要交叉编译就能用的库,可参考性不强 ...
- Asp-Net-Core开发笔记:集成Hangfire实现异步任务队列和定时任务
前言 最近把Python写的数据采集平台往.Net Core上迁移,原本的采集任务使用多进程+线程池的方式来加快采集速度,使用Celery作为异步任务队列兼具定时任务功能,这套东西用着还行,但反正就折 ...
- acwing346 走廊泼水节 (最小生成树)
完全图就是每两个点都有直接相连的边. 模拟Kruskal算法的过程,每选择一条边加入时,他两端端点在同一个集合中就跳过,否则考虑合并两个集合,合并时需要增加的每条边的权值至少是edge[i]+1,这才 ...
- SpringMVC访问静态资源的问题。
在项目中引用css和js文件一直出错,反复修改文件路径始终访问不到.究其原因原来是应该在web.xml 文件中添加访问静态资源的默认servlet. 如下. <servlet-mapping&g ...
- emqx启用JWT令牌认证(包含hmac-based和public-key)
emqx连接启用jwt令牌认证 jwt令牌 概述 JWT 即 JSON Web Tokens 是一种开放的,用于在两方之间安全地表示声明的行业标准的方法(RFC 7519). 组成 令牌的形式 xxx ...