最近项目中的一个耗时较长的作业出现CPU占用率高的问题。经过排查,发现主要消耗时间的是批量向MyBatis插入数据。映射器配置或多或少是通过foreach循环完成的。(由于项目保密,以下代码均为本人编写的demo代码)"model"index="index"separator=",">(#{model.id},#{model.name})这种方法提高批量插入速度的原理是使用传统的:INSERTINTO`table1`(`field1`,`field2`)VALUES("data1","data2");INSERTINTO`table1`(`field1`,`field2`)VALUES("data1","data2");INSERTINTO`table1`(`field1`,`field2`)VALUES("data1","data2");INSERTINTO`table1`(`field1`,`field2`)VALUES("data1","data2");INSERTINTO`table1`(`field1`,`field2`)VALUES("data1","data2");插入:INSERTINTO`table1`(`field1`,`field2`)VALUES("data1","data2"),("data1","data2"),("data1","data2"),("data1""数据2"),("数据1","数据2&“;”);MySqlDocs中也提到了这个技巧,如果你想优化插入速度,你可以将许多小操作组合成一个大操作。理想情况下,这可以在单个连接数据中一次发送许多新行,并推迟所有索引更新和一致性检查到最后。乍一看这个foreach没有问题,但是经过项目实践发现,当表的列数很大(20+),并且一次插入的行数很大(5000+),整个插入时间很长,达到了14分钟,让人受不了。数据中也提到了一句话:当然不要把ALLofthemcombinedALLofthem,iftheamountisHUGE.假设您需要插入1000行,那么不要一次插入一行。您不应该同样地尝试在单个查询中拥有所有1000行。而是将其分解成更小的尺寸。它强调的是,当insert数量较多时,不能一次放在一条语句中。但是为什么不能放在同一个句子里呢?为什么这个声明需要这么长时间?查了资料发现:InsertinsideMybatisforeach不是batch的,这是一个单条的(可能会变成巨型的)SQL语句,带来的弊端是:有些数据库如Oracle这里不支持。在相关情况下:将有大量记录要插入,并且将达到数据库配置的限制(默认情况下每个语句大约2000个参数),如果语句本身变得太大,最终可能会出现数据库堆栈错误。不能在mybatisXML中对集合进行迭代。只需在JavaForeach循环中执行一个简单的Insert语句。最重要的是sessionExecutor类型。SqlSessionsession=sessionFactory.openSession(ExecutorType.BATCH);for(Modelmodel:list){session.insert("insertStatement",model);}session.flushStatements();不像默认的ExecutorType。简单,该语句将准备一次并为每个要插入的记录执行。从数据可以看出,默认的executor类型是Simple,每条statement都会创建一个新的pre-executor处理语句,即创建PreparedStatement对象。在我们的项目中,会一直使用批量插入的方法,由于MyBatis不能对包含的语句使用缓存,所以每次调用该方法都会重启。解析sql语句。在内部,它仍然生成与上面的JDBC代码相同的带有许多占位符的单个插入语句。MyBatis有缓存PreparedStatement的能力,但是这条语句不能被缓存,因为它包含元素并且语句根据参数而变化。因此,MyBatis必须1)评估foreach部分和2)解析语句字符串以在每次执行此语句时构建参数映射[1]。当语句字符串很大并且包含很多占位符时,这些步骤是相对昂贵的过程。[1]简单的说,就是占位符和参数之间的映射。从以上信息来看,是耗时的。由于我在foreach之后有5000+个值,所以这个PreparedStatement很长,包含了很多占位符。字符,占位符和参数的映射尤其耗时。而且查阅相关资料发现,values的增长和需要解析的时间都是指数增长的。因此,如果非要使用foreach方式进行批量插入,可以考虑减少一次insert语句中取值的个数,最好达到上面曲线的底部值,这样速度最快。一般来说,根据经验,一次插入20-50行比较合适,时间消耗可以接受。重点来了。上面说的是,如果非要用来插入,可以提高性能。其实在MyBatis文档中写批量插入的时候,推荐使用另一种方式。(可以在http://www.mybatis.org/mybatis-dynamic-sql/docs/insert.html中看到BatchInsertSupport标题中的内容)SqlSessionsession=sqlSessionFactory.openSession(ExecutorType.BATCH);try{SimpleTableMappermapper=会话。getMapper(SimpleTableMapper.class);Listrecords=getRecordsToInsert();//notshownBatchInsertbatchInsert=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();}即基本思路是设置MyBatissession的executor类型为Batch,然后多次执行insert语句。类似于JDBC的如下语句。Connectionconnection=DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb?useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=false&rewriteBatchedStatements=true","root","root");connection.setAutoCommit(false);PreparedStatementps=connection.prepareStatement("insertintotb_user(name)values(?)");for(inti=0;i插入,需要控制每次插入的记录数在20~50条左右。