数据批处理优化
前言
当我们临时需要对大量数据进行操作入库时,怎么才能最快有效的完成工作然后摸鱼呢?那当然就是开启批处理了(主要是较为简单的业务数据处理,如果业务需要更加系统的批处理操作,可以了解下spring-batch,如果只是数据库之间的数据迁移同步可以使用kettle工具),先直接说怎么开启批处理吧,后面介绍测试结果及原理
非常简单,直接在yml配置文件的url加上
rewriteBatchedStatements=true
#案列:
url: jdbc:mysql://127.0.0.1:3306/demo?rewriteBatchedStatements=true
#如果是mybatis框架也可以选择如下这种方式,都一样
# mybatis:
# executor-type: batch
原理
当用mybatis-plus批量插入方法时,其实发现速度很慢,是因为mybatis提供了三种sql执行器,默认的是SIMPLE. 普通批量插入也就相当于是每个语句都会创建一个新预处理器,那也就是每个语句都要执行一套完整地插入流程,什么会话,缓存,事务等等
而优化的批处理,则是将执行器改为BATCH,它就相当于只开启一个预处理器,重复加载好所有的sql语句,然后一次性提交执行
这里讲一下<foreach>这种方法,它会比普通批量插入好点,会转换成一条sql语句,变成 " insert into (x1,x2,x3) values (y1,y2,y3),(t1,t2,t3)... " 这种形式,但性能也不行,具体参考文章三
测试
目前测试由快到慢的顺序如下:
原生开启batch模式 > mytais开启batch模式 > mytais 非batch状态foreach 模式 > 普通模式
这里有个原生batch模式,就是 原生写法操作,是最快的,我自己测试的数据如下:
条数 | 普通batch | 原生batch |
---|---|---|
5千 | 12s-13s | 0.3-0.4 |
5万 | 2-3s | 0.4-0.6 |
测的有点拉胯,但也能大概看出差距了,看网上说原生写法快,但没想到这么快,所以如果是一次性使用的话那就肯定使用原生来玩了,原生的代码如下:
// 从spring管理的数据源中直接拿
@Resource(name = "dataSource")
private DataSource dataSource;
@RequestMapping("demo")
@ApiOperation("测试批量插入")
Object test() {
try {
// 通过驱动管理类获取数据库链接
// Class.forName("com.mysql.jdbc.Driver");
// Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
Connection connection = dataSource.getConnection();
connection.setAutoCommit(false);
String sql = "INSERT INTO demo (name,age,birthday) VALUES(?,?,?) ";
PreparedStatement statement = connection.prepareStatement(sql);
for (int i = 0; i < 50000; i++) {
statement.setString(1, "张三");
statement.setString(2, "年龄:" + i);
statement.setDate(3, new Date(System.currentTimeMillis()));
statement.addBatch();
}
long start = System.currentTimeMillis();
statement.executeBatch();
connection.commit();
statement.close();
connection.close();
System.out.print("耗时:");
System.out.println(System.currentTimeMillis() - start);
return System.currentTimeMillis() - start;
}catch (Exception e){
e.printStackTrace();
}
return null;
}