分批批量插入数据(基于Mybatis的实现-Oracle)

-----------------------------------

20170528 第二次编辑:主要是补充mysql相关内容。

-----------------------------------

mysql支持batch提交改进方案:声明:mysql仍然没有内部游标,让数据库支持executeBatch的方式处理。

MySql 的批量操作,要加rewriteBatchedStatements参数

引用“

MySql的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。

例如: String connectionUrl="jdbc:mysql://192.168.1.100:3306/test?rewriteBatchedStatements=true" ; 

还要保证Mysql JDBC驱的版本。MySql的JDBC驱动的批量插入操作性能是很优秀的。

原文链接:http://elf8848.iteye.com/blog/770032  (建议去看看这个链接的评论区)

参考资料:https://www.oschina.net/question/2553117_2162171?sort=time 

20170528亲测,插入26663条数据,

加上rewriteBatchedStatements后,耗时:3734毫秒
不加rewriteBatchedStatements前,耗时:672551毫秒

mysql装在本机上,行字段数多,仅从本次测试看,性能提高了180倍。

 ---------------------------------------------------

第一版:20170526 原创

 ---------------------------------------------------

前言:做一个数据同步项目,要求:同步数据不丢失的情况下,提高插入性能。

项目DB框架:Mybatis。DataBase:Oracle。

----------------------------------------------------------------------------

批量插入数据方式:

一、Mybatis 全局设置批处理;

二、Mybatis 局部设置批处理;

三、Mybatis foreach批量插入:

①SELECT UNION ALL;

②BEGIN INSERT INTO ...;INSERT INTO...;...;END;

四、java自带的批处理插入;

五、其他方式

-----------------------------------------------------------------------------

先说结论:Mybatis(全局/局部)批处理和java自带的批处理 性能上差不多,属于最优处理办法,我这边各种测试后,最后采用Mybatis局部批处理方式。

一、Mybatis 全局设置批处理

先上Spring-Mybatis.xml 配置信息

Spring-Mybatis.xml

再上mybatisConfig.xml(在本项目中,我没有设置setting。最终采用的局部批处理,因此未设置全局批处理,具体原因后面再说。)

mybatisConfig.xml

这样子设置好后,在BaseService开放saveBatch(List<T> list)方法

BaseService.saveBatch(List list)

虽然看上去是500条记录,一次次INSERT INTO,但由于在全局已经设置Mybatis是批处理执行器,所以这500条INSERT INTO只会与Oracle数据库通信一次。

全局设置批处理的局限性在哪里呢?

先附上mybatis官方的讨论列表中最很关键的一句:“If the BATCH executor is in use, the update counts are being lost. ”

设置全局批处理后,DB里的insert、Update和delete方法,都无法返回进行DML影响DB_TABLE的行数。

1.insert 无法返回影响的行数,这个好解决,一个批处理放在一个事务里,记录批处理失败次数,总数-批处理失败次数*单位批处理数据量,就能得到insert 影响DB_TABLE的行数;

2.但是update和delete就无法很简单的去统计影响行数了,如果做反复查询,反而降低了效率,得不偿失。

虽现在的项目尚未有需要反馈影响DB_TABLE行数的需求,但是为了更灵活,我们放弃了全局批处理的方式。

!这里提个疑问:为什么Mybatis官方,不将批处理的选择方式下沉到方法级别?方便开发者根据实际情况,灵活选择。我觉得这是个可以改进的地方,如有机会,可看源码去进行改进。

---------------------------------------------------------------------------------------------------------

二、Mybatis局部批处理方式

由于领导说全局批处理方式,不够灵活,不适宜项目所需,要另想办法支持。但是java自带的批处理,因为项目代码管理的要求,也不能采用。因此,在仔细阅读官方文档后,设想自己能否获取SQLSession后openSession,将这个会话设置为批处理呢?

先看MyBatis官方网站(须FanQiang):http://www.mybatis.org/mybatis-3/zh/getting-started.html

官方建议的写法

后查阅Mybatis java API(须FanQiang):  http://www.mybatis.org/mybatis-3/zh/java-api.html

现在你有一个 SqlSessionFactory,可以用来创建 SqlSession 实例。

SqlSessionFactory

SqlSessionFactory 有六个方法可以用来创建 SqlSession 实例。通常来说,如何决定是你 选择下面这些方法时:

  • Transaction (事务): 你想为 session 使用事务或者使用自动提交(通常意味着很多 数据库和/或 JDBC 驱动没有事务)?
  • Connection (连接): 你想 MyBatis 获得来自配置的数据源的连接还是提供你自己
  • Execution (执行): 你想 MyBatis 复用预处理语句和/或批量更新语句(包括插入和 删除)

重载的 openSession()方法签名设置允许你选择这些可选中的任何一个组合。

官方提供的openSession方法

因此出来了局部批处理第一套代码实现方式:

sqlSession(List data)
mybatis-dataSource.xml

已经在Spring-Mybatis.xml 中配置了SQLSessionFactory,那我为何还要自己去创建SQLSessionFactory呢?因此继续改良代码

复制代码
 1 public static void mybatisSqlSession(List<Student> data){
 2         DefaultSqlSessionFactory sqlSessionFactory = (DefaultSqlSessionFactory) ServiceBeanConstant.CTX.getBean("sqlSessionFactory");
 3         SqlSession batchSqlSession = null;
 4         try{
 5             batchSqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
 6             int batchCount = 500;//每批commit的个数
 7             for(int index = 0; index < data.size();index++){
 8                 Student stu = data.get(index);
 9                 batchSqlSession.getMapper(StudentMapper.class).insert(stu);
10                 if(index !=0 && index%batchCount == 0){
11                     batchSqlSession.commit();
12                 }
13             }
14             batchSqlSession.commit();
15         }catch (Exception e){
16             e.printStackTrace();
17         }finally {
18             if(batchSqlSession != null){
19                 batchSqlSession.close();
20             }
21         }
22     }
复制代码

这个版本的局部批处理插入是比较满意的,最终采用的方式也是这个版本。

下面放出在IService接口定义和Service的具体实现代码:

IService接口定义

复制代码
1 /**
2      * 批处理插入数据(方法内部定义500条为一个批次进行提交)
3      * 使用注意事项:必须在XxxMappper.xml中实现<insert id="insert" ...>....<insert/>的sql
4      * @param data 批量插入的数据
5      * @param mClass 调用的XxxMaperr.class
6      * @auth robin
7      * Created on 2016/3/14
8      */
9     void saveBatch(List<T> data,Class mClass);
复制代码

Service实现

按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

局部和全局批处理插入对比:局部批处理,可以对特定一类的方法,进行数据批处理,不会影响其他DML语句,其他DML语句,可以正常返回影响DB_TABLE的行数。

!这样既能针对特殊需求(批处理)支持,也能支持未来需要返回影响数据行的要求。

注意:使用批处理方式进行DML操作,是无法反馈影响DB_TABLE行数的数据。无论是局部批处理还是java自带的批处理方式,皆无法反馈DB_TABLE count。

补充完善:

在我的Service实现中,通过注入的方式,获取mapper的实例

Service

前面的Service saveBatch方法中,还需要传入指定的Mapper.class.对本项目其他开发者来说,与之前的环境相比,多传一个参数感觉别扭。

那么为何我不继续封装,外部无需传入Mapper.class,而是通过内部注入的mapper实例获取Mapper.class.
改良后的代码:

saveBatch(List data)

这里对mapper实例进行一个简短的说明:

1.mapper实例是通过java动态代理来实例化的;

2.mapper的SQLSession是使用mybatis统一的配置实例的;

3.mapper的默认执行器是SIMPLE(普通的执行器);

-------------------------------------------------------------------------------------

 三、Mybatis foreach批量插入

Mybatis foreach 批量插入,如果批量插入的数据量大,不得不说这真是一个非常糟糕的做法

无论是SELECT ** UNION ALL 还是BEGIN ...;END; ,相对而言后者比前者稍微好点。

放出DB和我测试的结果:

耗时 占当时整个数据库CPU百分比 说明
15.5 98.33 union all方式拼接插入
16.4 97.75 begin end方式插入块
1.54 64.81 java 自带的batch方式插入

①foreach union all的批量插入,现已有大量的博客资源可供参考,我就不贴出自己的实现方式了。

如果有兴趣可以参阅:http://blog.csdn.net/sanyuesan0000/article/details/19998727 (打开浏览器,复制url)

这篇博客。BEGIN END的方式,也是从这篇博客中得到启发。只不过他是把BEGIN END用在update中。

②foreach begin end 语句块

我的实现:

insertBatch

 调用方式:

saveBatch(List list)

---------------------------------------------------------------------

四、java自带的批处理方式

废话不多说,直接上代码

JDBC BATCH

这种批量插入大量数据的方式,性能上最好。但是因为我们小组代码管理所限制,因此这种方式不使用。

------------------------------------------------------------------------

五、其他方式

现在已经忘了,其他方式到底使用过哪些,但总归是比以上四种效果都更差,所以没什么印象了。

如果各位,还有什么其他更好的批量插入数据的方式,欢迎加入讨论,集思广益。 

以上就是这两天,对在原项目基础上不进行大变动的基础上,提供批处理插入数据的所思所行。

转自:https://www.cnblogs.com/robinjava77/p/5530681.html

其他资料:
https://blog.csdn.net/wlwlwlwl015/article/details/50246717

猜你喜欢

转载自blog.csdn.net/dragonpeng2008/article/details/79716102