1、问题描述
项目用的是Mybatis Plus框架操作数据库,在使用batchSave批量插入方法的时候发现效率极低,插入2w数据花了6分钟,太恐怖了。
看了源码发现,项目的批量插入方法调用的是Mybatis Plus的BatchExcutor,用这个本意是将多次更新sql语句集合为一条更新语句,复用同一个sql连接更新数据。但是打印sql语句发现,实际上仍然是一条一条插入的,只不过是复用同一个prepareStatement而已。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eYdXHEWB-1643113459225)(resource/image-20220124202735365.png)]
2、问题解决
方案一:之所以没有按照预期的拼接sql为一条是因为我们需要在连接数据库的时候设置参数 rewriteBatchedStatements
url: jdbc:mysql://${datasource.host}/${datasource.name}??rewriteBatchedStatements=true
方案二:配置解决的方式太麻烦,在微服务情况下又要改很多地方,所以我采用了第二种方案也是mybatis plus 提供的sql注入器
- 继承框架提供的
DefaultSqlInjector
,实现自己的sql注入器
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
methodList.add(new InsertBatchSomeColumn()); // 加入批量插入方法
return methodList;
}
InsertBatchSomeColumn
也是框架提供的批量插入方法,一句话来讲就是将我们的多条插入sql拼接成一条sql,这里就不插代码了,当然你也可以自己写一个方法继承AbstractMethod
- 继承BaseMaper,加上sql注入的方法名接口即可使用
public interface InjectionMapper<T> extends BaseMapper<T> {
Integer insertBatchSomeColumn(Collection<T> entityList);
int batchSize = 1000; // 应为mysql对于太长的sql语句是有限制的,所以我这里设置每1000条批量插入拼接sql
default Integer batchInsert(Collection<T> entityList) {
int result = 0;
Collection<T> tempEntityList = new ArrayList<>();
int i = 0;
for (T entity : entityList) {
tempEntityList.add(entity);
if (i > 0 && (i % batchSize == 0)) {
result += insertBatchSomeColumn(tempEntityList);
tempEntityList.clear();
}
i++;
}
if (ValidateUtil.isNotEmpty(tempEntityList)) {
result += insertBatchSomeColumn(tempEntityList);
}
return result;
}
}
3、总结
其实两种方法底层都是将sql语句拼接起来,只不过一个是我们手动拼接,一个是mysql帮我们拼接
其实还有一种方式就是在xml中写动态sql,foreach标签拼接,但是太麻烦了不是。而且也是拼接sql的方式
我看网上对于这种方法遇到过一个问题就是主键id自增出错的问题,但是我的id是自己设置的所以没有问题,在这里提个醒
上图是我修改后,插入效率的提升,可以说是指数倍提升吧,有用的话点个赞哦。