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

MyBatis批量插入几千条数据,请慎用foreach!!!

0
分享至

作者:huanghanqian

来源:blog.csdn.net/huanghanqian/article/details/83177178

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


"batchInsert" parameterType="java.util.List"> 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, t his 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 左右。

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

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.

相关推荐
热点推荐
驻港高官:贪腐卖国阻止回归,被调查时携女人叛逃美国,结局如何

驻港高官:贪腐卖国阻止回归,被调查时携女人叛逃美国,结局如何

阿胡
2024-04-28 11:34:37
新冠疫苗之父杨晓明被抓,带队研制科兴疫苗,曾9个月抽60管血!

新冠疫苗之父杨晓明被抓,带队研制科兴疫苗,曾9个月抽60管血!

闲事杂说
2024-04-28 02:12:03
4月份马上结束,养老金调整方案怎么还没公布?今年还会上调吗?

4月份马上结束,养老金调整方案怎么还没公布?今年还会上调吗?

云姐闲聊
2024-04-28 12:01:16
无奈空砍!恩比德19中7得27分10板6助3断2帽 末节仅得1分

无奈空砍!恩比德19中7得27分10板6助3断2帽 末节仅得1分

直播吧
2024-04-29 04:02:31
塞力斯问界M7声明与博世冲突,谁的谎言如此不堪?谁在玩弄车主?

塞力斯问界M7声明与博世冲突,谁的谎言如此不堪?谁在玩弄车主?

美芳
2024-04-28 22:27:57
美国版五四运动?网传在美国各高校,陈独秀的《新青年》被学生疯传

美国版五四运动?网传在美国各高校,陈独秀的《新青年》被学生疯传

不掉线电波
2024-04-28 18:09:09
笑麻了!OPPO女经理测试手机,“高跟暴力”那一刻俘获太多男人心

笑麻了!OPPO女经理测试手机,“高跟暴力”那一刻俘获太多男人心

番茄娱乐加
2024-04-27 13:25:52
丈夫、弟弟、儿子均遇难!家属质疑“华为问界M7刚买仨月事故后起火”,回应→

丈夫、弟弟、儿子均遇难!家属质疑“华为问界M7刚买仨月事故后起火”,回应→

鲁中晨报
2024-04-28 15:49:03
不爱国?无人在乎神舟十八号发射,网友:房贷、车贷把我压垮!

不爱国?无人在乎神舟十八号发射,网友:房贷、车贷把我压垮!

鹏飞深文
2024-04-28 14:16:14
上月前往中央任职的候补中央委员,再添新身份!

上月前往中央任职的候补中央委员,再添新身份!

上观新闻
2024-04-28 17:17:53
贾跃亭称已还清100亿美元债务,却被大V光速打脸:还了跟没还一样

贾跃亭称已还清100亿美元债务,却被大V光速打脸:还了跟没还一样

可达鸭面面观
2024-04-28 15:28:34
田馥甄内地复出失败!音乐节宣布取消其演出,本人发声强撑体面

田馥甄内地复出失败!音乐节宣布取消其演出,本人发声强撑体面

萌神木木
2024-04-27 22:17:58
花990万拍下周鸿祎二手迈巴赫的男子系北京二手车商,曾喊话“1000咱都要”,笑称没有超过预算

花990万拍下周鸿祎二手迈巴赫的男子系北京二手车商,曾喊话“1000咱都要”,笑称没有超过预算

极目新闻
2024-04-28 21:17:03
330天研发疫苗盈利7000亿!杨晓明被查:打3针北京生物者说感恩

330天研发疫苗盈利7000亿!杨晓明被查:打3针北京生物者说感恩

大风文字
2024-04-28 08:15:57
大学教授:年轻人大搞反以色列抗议,只因没有足够的性生活

大学教授:年轻人大搞反以色列抗议,只因没有足够的性生活

小星球探索
2024-04-28 12:53:26
990万元!周鸿祎迈巴赫成交!半个车圈到场

990万元!周鸿祎迈巴赫成交!半个车圈到场

证券时报e公司
2024-04-28 19:42:38
布伦森季后赛单场至少得到40分10助 尼克斯队史第一人!

布伦森季后赛单场至少得到40分10助 尼克斯队史第一人!

直播吧
2024-04-29 04:02:29
女人在过夫妻性生活时,为什么总发出声音?医生:大多数人不了解

女人在过夫妻性生活时,为什么总发出声音?医生:大多数人不了解

皮皮讲文
2024-01-02 10:36:17
哈尔滨一小区楼体“突然裂开、逐渐倾斜”?官方通报:决定拆除!

哈尔滨一小区楼体“突然裂开、逐渐倾斜”?官方通报:决定拆除!

鲁中晨报
2024-04-28 08:05:03
王岐山同志:毛主席真有远见!

王岐山同志:毛主席真有远见!

日风的故事屋
2024-04-28 21:11:12
2024-04-29 04:30:44
会呼吸的Coder
会呼吸的Coder
科技改变世界
466文章数 1813关注度
往期回顾 全部

科技要闻

特斯拉生死时速,马斯克西天取经

头条要闻

警方通报女子在卫生间被打:4人被行拘13日罚款1千元

头条要闻

警方通报女子在卫生间被打:4人被行拘13日罚款1千元

体育要闻

湖人的G4,尽人事得到了回报

娱乐要闻

张杰谢娜发文为何炅庆生,亲如家人!

财经要闻

上财万字报告深度解读Q1经济

汽车要闻

鸿蒙首款行政旗舰轿车 华为享界S9实车亮相车展

态度原创

艺术
房产
游戏
时尚
军事航空

艺术要闻

共度北京108小时 北京当代2024“凝聚”全球36座城市100余家艺术机构

房产要闻

力度越来越大!落户两年享本地居民购房政策,海南第16城松绑限购!

网友认为《剑星》拿不到年度最佳:打不过《FF7重生》

裤子+小猫跟,这个组合气质又好看

军事要闻

也门胡塞击落美军"死神"无人机 并展示残骸

无障碍浏览 进入关怀版