网易首页 > 网易号 > 正文 申请入驻

还用foreach在MyBatis批量插入数据,服务器还没崩?

0
分享至

近日,项目中有一个耗时较长的Job存在CPU占用过高的问题,经排查发现,主要时间消耗在往MyBatis中批量插入数据。

近日,项目中有一个耗时较长的Job存在CPU占用过高的问题,经排查发现,主要时间消耗在往MyBatis中批量插入数据。mapper configuration是用foreach循环做的,差不多是这样。(由于项目保密,以下代码均为自己手写的demo代码)

insert into USER (id, name) values

(#{model.id}, #{model.name})

这个方法提升批量插入速度的原理是,将传统的:

INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");

转化为:

INSERT INTO `table1` (`field1`, `field2`)
VALUES ("data1", "data2"),
("data1", "data2"),
("data1", "data2"),
("data1", "data2"),
("data1", "data2");

在MySql Docs中也提到过这个trick,如果要优化插入速度时,可以将许多小型操作组合到一个大型操作中。理想情况下,这样可以在单个连接中一次性发送许多新行的数据,并将所有索引更新和一致性检查延迟到最后才进行。

乍看上去这个foreach没有问题,但是经过项目实践发现,当表的列数较多(20+),以及一次性插入的行数较多(5000+)时,整个插入的耗时十分漫长,达到了14分钟,这是不能忍的。在资料中也提到了一句话:

Of course don't combine ALL of them, if the amount is HUGE. Say you have 1000 rows you need to insert, then don't do it one at a time. You shouldn't equally try to have all 1000 rows in a single query. Instead break it into smaller sizes.

它强调,当插入数量很多时,不能一次性全放在一条语句里。可是为什么不能放在同一条语句里呢?这条语句为什么会耗时这么久呢?我查阅了资料发现:

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.

SqlSession session = sessionFactory.openSession(ExecutorType.BATCH);
for (Model model : list) {
session.insert("insertStatement", model);
}
session.flushStatements();

Unlike default ExecutorType.SIMPLE, the statement will be prepared once and executed for each record to insert.

从资料中可知,默认执行器类型为Simple,会为每个语句创建一个新的预处理语句,也就是创建一个PreparedStatement对象。在我们的项目中,会不停地使用批量插入这个方法,而因为MyBatis对于含有

的语句,无法采用缓存,那么在每次调用方法时,都会重新解析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 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 的方式来进行批量插入的话,可以考虑减少一条 insert 语句中 values 的个数,最好能达到上面曲线的最底部的值,使速度最快。一般按经验来说,一次性插20~50行数量是比较合适的,时间消耗也能接受。

重点来了。上面讲的是,如果非要用

的方式来插入,可以提升性能的方式。而实际上,MyBatis文档中写批量插入的时候,是推荐使用另外一种方法。(可以看 http://www.mybatis.org/mybatis-dynamic-sql/docs/insert.html 中 Batch Insert Support 标题里的内容)

SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
SimpleTableMapper mapper = session.getMapper(SimpleTableMapper.class);
List

records = getRecordsToInsert(); // not shown BatchInsert

batchInsert = insert(records) .into(simpleTable) .map(id).toProperty("id") .map(firstName).toProperty("firstName") .map(lastName).toProperty("lastName") .map(birthDate).toProperty("birthDate") .map(employed).toProperty("employed") .map(occupation).toProperty("occupation") .build() .render(RenderingStrategy.MYBATIS3); batchInsert.insertStatements().stream().forEach(mapper::insert); session.commit(); } finally { session.close(); }

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

Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb?useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=false&rewriteBatchedStatements=true","root","root");
connection.setAutoCommit(false);
PreparedStatement ps = connection.prepareStatement(
"insert into tb_user (name) values(?)");
for (int i = 0; i < stuNum; i++) {
ps.setString(1,name);
ps.addBatch();
}
ps.executeBatch();
connection.commit();
connection.close();

经过试验,使用了 ExecutorType.BATCH 的插入方式,性能显著提升,不到 2s 便能全部插入完成。

总结一下,如果MyBatis需要进行批量插入,推荐使用 ExecutorType.BATCH 的插入方式,如果非要使用

的插入的话,需要将每次插入的记录控制在 20~50 左右。

  • 原文来自:https://developer.51cto.com/art/202111/691209.htm
  • 本文地址:https://www.linuxprobe.com/foreach-mybatis-data.html编辑:清蒸github,审核员:逄增宝
  • Linux命令大全:https://www.linuxcool.com/

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
女子出轨遭男友捉奸暴打,对方一丝不挂,现场画面曝光太辣眼

女子出轨遭男友捉奸暴打,对方一丝不挂,现场画面曝光太辣眼

180°视角
2024-05-23 15:42:47
笑喷!这就是被制裁的俄罗斯吗?网友:中粮这波操作简直赢麻了

笑喷!这就是被制裁的俄罗斯吗?网友:中粮这波操作简直赢麻了

莫将离
2024-05-21 23:36:03
520当天结婚1.4万对,比去年1.6万激增13.4%?四川民政闹出个数学笑话

520当天结婚1.4万对,比去年1.6万激增13.4%?四川民政闹出个数学笑话

可达鸭面面观
2024-05-23 16:35:02
中国一旦进入战争,这些数字告诉你,只有一个结论:缴械投降

中国一旦进入战争,这些数字告诉你,只有一个结论:缴械投降

蓝婉莹
2024-05-12 02:22:39
湖人将与马刺竞争保罗!可用里夫斯+八村垒换加兰 选帅进展缓慢

湖人将与马刺竞争保罗!可用里夫斯+八村垒换加兰 选帅进展缓慢

罗说NBA
2024-05-24 04:52:52
于北辰:统一不能喊口号,而是要让两岸所有中国人都过上好日子

于北辰:统一不能喊口号,而是要让两岸所有中国人都过上好日子

李博世财经
2024-05-23 20:21:39
以色列总理,把公知和美国一下都干傻眼了

以色列总理,把公知和美国一下都干傻眼了

韬闻
2024-05-23 13:02:36
出镜忏悔的“老虎”敛财超4亿!曾说想“搞一点大动静”来“引起上级注意”

出镜忏悔的“老虎”敛财超4亿!曾说想“搞一点大动静”来“引起上级注意”

政知新媒体
2024-05-23 18:41:06
重新出山!穆帅3进宫,执教英超5冠王,目标争前4,引进意甲神锋

重新出山!穆帅3进宫,执教英超5冠王,目标争前4,引进意甲神锋

体坛春秋
2024-05-23 22:25:23
罗森宣布将于7月24日退市,在华门店超6000家

罗森宣布将于7月24日退市,在华门店超6000家

红星新闻
2024-05-23 19:32:35
姑娘以后别穿这么紧的裤子上街了,走在你身后都不知道往哪看,太尴尬了哈哈哈

姑娘以后别穿这么紧的裤子上街了,走在你身后都不知道往哪看,太尴尬了哈哈哈

匹夫来搞笑
2024-04-28 07:50:20
庆余年2唯一输家:最牛星二代跌下神坛,演技尴尬,全程被吊打

庆余年2唯一输家:最牛星二代跌下神坛,演技尴尬,全程被吊打

糊咖娱乐
2024-05-23 11:48:07
使劲折腾:多地出现俄文路牌

使劲折腾:多地出现俄文路牌

据说说娱乐
2024-05-23 22:43:05
“奇技淫巧”四个字又出来了

“奇技淫巧”四个字又出来了

报人刘亚东
2024-05-23 16:29:02
伊朗总统座机失事后,飞行员的电话为何接通?

伊朗总统座机失事后,飞行员的电话为何接通?

中国新闻周刊
2024-05-23 19:04:10
上海总和生育率仅0.6!低于韩国!全市600万育龄女性,仅376万女性生育孩子

上海总和生育率仅0.6!低于韩国!全市600万育龄女性,仅376万女性生育孩子

可达鸭面面观
2024-05-23 17:35:19
解放军台海出击,蒋万安称不接受,朱立伦见美高官,台军要硬刚?

解放军台海出击,蒋万安称不接受,朱立伦见美高官,台军要硬刚?

战域笔墨
2024-05-24 00:04:20
明明早被查出致癌,美国、加拿大已经下架,却仍在中国市场销售!

明明早被查出致癌,美国、加拿大已经下架,却仍在中国市场销售!

南风西洲
2024-05-22 22:01:20
中外舆论出现大反转

中外舆论出现大反转

智先生
2024-05-23 21:09:08
一下子败光了这几年对刘畊宏的所有好感

一下子败光了这几年对刘畊宏的所有好感

圈里的甜橙子
2024-05-23 21:53:23
2024-05-24 07:40:49
孙有匪
孙有匪
科技
1595文章数 2009关注度
往期回顾 全部

科技要闻

黄仁勋业绩会万字实录:我们的压力太大了

头条要闻

赖清德就职第3天解放军就围岛军演 台媒晒出1张对比图

头条要闻

赖清德就职第3天解放军就围岛军演 台媒晒出1张对比图

体育要闻

欧文,三十二而立

娱乐要闻

大S儿子被学校退学,张兰称孙子没人管

财经要闻

九鼎金租减值罗生门:郑州银行藏雷?

汽车要闻

上汽大通大家7超混/大家9超混将于6月7日正式上市

态度原创

教育
亲子
艺术
游戏
军事航空

教育要闻

你知道哪些有名的定律?

亲子要闻

儿子孙子面前,爷爷变脸的速度太快,隔辈亲有些过于明显!

艺术要闻

穿越时空的艺术:《马可·波罗》AI沉浸影片探索人类文明

Xbox提醒不要错过6月发布会:有《使命召唤21》

军事要闻

以军继续杰宁攻势 巴武装组织打击以目标

无障碍浏览 进入关怀版