测试表结构
create table batch_test ( id int auto_increment primary key, username varchar(20) not null, password varchar(100) not null, address varchar(100) null );
随机生成10w条数据
private List<BatchTestDO> generateData() {
List<BatchTestDO> list = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
BatchTestDO entity = new BatchTestDO();
entity.setUsername("小明" + i);
entity.setPassword(UUID.randomUUID().toString());
entity.setAddress(UUID.randomUUID().toString());
list.add(entity);
}
return list;
}
1.mybatis的BatchExecutor
数据库连接配置:url: jdbc:mysql://127.0.0.1:3306/test?rewriteBatchedStatements=true
,主要就是rewriteBatchedStatements
这个参数
@Test
public void test001() throws Exception {
List<BatchTestDO> data = generateData();
long start=System.currentTimeMillis();
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
batchTestMapper=session.getMapper(BatchTestMapper.class);
for (BatchTestDO datum : data) {
batchTestMapper.insert(datum);
}
session.commit();
System.out.println(String.format("用时:%d",System.currentTimeMillis()-start));
}
十万数据插入4634ms
2.用jdbc驱动原始的批量提交方式
@Test
public void test002() throws SQLException {
List<BatchTestDO> data = generateData();
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?rewriteBatchedStatements=true","root","123456abc");
long start=System.currentTimeMillis();
connection.setAutoCommit(false);
PreparedStatement ps = connection.prepareStatement(
"insert into batch_test (username,password,address) values(?,?,?)");
for (int i = 0; i < data.size(); i++) {
BatchTestDO batchTestDO=data.get(i);
ps.setString(1,batchTestDO.getUsername());
ps.setString(2,batchTestDO.getPassword());
ps.setString(3,batchTestDO.getAddress());
ps.addBatch();
}
ps.executeBatch();
connection.commit();
System.out.println(String.format("用时:%d",System.currentTimeMillis()-start));
connection.close();
}
只用了2486ms
3.用foreach标签的方式
@Insert("<script>" +
"insert into batch_test (username,password,address) values" +
"<foreach collection='list' item='entity' separator=','>" +
"(#{entity.username},#{entity.password},#{entity.address})" +
"</foreach>" +
"</script>")
int batchInsert(@Param("list") List<BatchTestDO> list);
@Test
public void test003(){
List<BatchTestDO> data = generateData();
long start=System.currentTimeMillis();
batchTestMapper.batchInsert(data);
System.out.println(String.format("用时:%d",System.currentTimeMillis()-start));
}
用了5303ms
4.mybatis-plus的批量插入方式
@Test
public void test004(){
List<BatchTestDO> data = generateData();
long start=System.currentTimeMillis();
batchTestMapper.insertBatchSomeColumn(data);
System.out.println(String.format("用时:%d",System.currentTimeMillis()-start));
}
用了5163ms
以上程序执行的时候,打开数据库的general_log日志配置,可以看到他们发送到mysql服务端的sql语句都是一样的,insert into table (a,b,c) values (x,x,x),(x,x,x)...
这样的
测试用例2的速度比1 3 4都快的原因,个人认为应该是在于mybatis框架在读取对应字段值的时候用的java反射导致的耗时。
接下来说下rewriteBatchedStatements
的作用,这个参数的意义是:在底层jdbc是通过测试用例2这样的批量处理的方式插入数据的时候,如果rewriteBatchedStatements
设为true,jdbc驱动就会把sql在本地客户端拼接成insert into table (a,b,c) values (x,x,x),(x,x,x)...
这样的sql发送到服务端(这里注意,数据量很多的时候,并不会把所有数据拼接成一条sql,而是分批次拼接,因为mysql每次传输的sql大小是有限制的),否则测试用例2这样的批量插入和一条一条数据的插入没有任何区别。
接下来就把rewriteBatchedStatements
去掉再测一遍
290秒。。。。。
43秒
5535ms
5686ms