问题场景:
使用MyBatis-plus的saveBatch方法执行数据批量insert
问题描述:
/**
* 批量添加设备
* @param deviceList
* @param applicationName
* @return
*/
public boolean saveBatchDevice(List<Device> deviceList, String applicationName) {
if (CollectionUtils.isEmpty(deviceList)) {
return false;
}
boolean result = saveBatch(deviceList);
if (result) {
List<Map> mapList = deviceList.stream().map((Function<Device, Map>) device -> {
Map<String, String> item = new HashMap<>(5);
item.put("imei", device.getImei());
item.put("supplier", device.getSupplier());
item.put("model", device.getModel());
return item;
}).collect(Collectors.toList());
kafkaTemplate.send(String.format(ADD_DEVICE_TOPIC, applicationName), JSONUtil.toStr(mapList));
}
return result;
}
----------------------------------
调用该方法对传入的设备信息列表进行批量保存,但是发现速度非常慢,传入1000条数据保存用了大概1分钟,通过使用StopWatch记录各个流程的耗时,发现所有耗时全部在saveBatch上,这是该Service继承MyBatis-plus的ServiceImpl类后,获得的批量保存数据的方法。
原因分析:
查看该saveBatch源码实现
/**
* 执行批量操作
*
* @param entityClass 实体类
* @param log 日志对象
* @param list 数据集合
* @param batchSize 批次大小
* @param consumer consumer
* @param <E> T
* @return 操作结果
* @since 3.4.0
*/
public static <E> boolean executeBatch(Class<?> entityClass, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
Assert.isFalse(batchSize < 1, "batchSize must not be less than one");
return !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, sqlSession -> {
int size = list.size();
int i = 1;
for (E element : list) {
consumer.accept(sqlSession, element);
if ((i % batchSize == 0) || i == size) {
sqlSession.flushStatements();
}
i++;
}
});
}
将传入的实体List分为1000个一批,每个调用sqlSession.insert(sqlStatement, entity),insert完一批做一次sqlSession.flushStatements(),看起来是没有问题,但是就是速度非常慢。查阅相关资料发现,要批量执行的话,JDBC连接URL字符串中需要新增一个参数:rewriteBatchedStatements=true
MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。
MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。
只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL
另外这个选项对INSERT/UPDATE/DELETE都有效
解决方案:
在jdbc连接url最后加上jdbc:mysql://172.29.1.100:5000/bd_cloud_vehicle_dev?rewriteBatchedStatements=true,测试发现速度果然提升