前言

通过Mybatis做7000+数据量的批量插入的时候报错了,error log如下:

   ,
('G61010352',
'610103199208291214',
'学生52',
'G61010350',
'610103199109920192',
'学生50',
'07',
'01',
'0104',
' ',
0,
' ',
' ',
current_timestamp,
current_timestamp
) 被中止,呼叫 getNextException 以取得原因。at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2743)at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:411)at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2892)at com.alibaba.druid.filter.FilterChainImpl.statement_executeBatch(FilterChainImpl.java:2596)at com.alibaba.druid.wall.WallFilter.statement_executeBatch(WallFilter.java:473)at com.alibaba.druid.filter.FilterChainImpl.statement_executeBatch(FilterChainImpl.java:2594)at com.alibaba.druid.filter.FilterAdapter.statement_executeBatch(FilterAdapter.java:2474)at com.alibaba.druid.filter.FilterEventAdapter.statement_executeBatch(FilterEventAdapter.java:279)at com.alibaba.druid.filter.FilterChainImpl.statement_executeBatch(FilterChainImpl.java:2594)at com.alibaba.druid.proxy.jdbc.StatementProxyImpl.executeBatch(StatementProxyImpl.java:192)at com.alibaba.druid.pool.DruidPooledPreparedStatement.executeBatch(DruidPooledPreparedStatement.java:559)at org.apache.ibatis.executor.BatchExecutor.doFlushStatements(BatchExecutor.java:108)at org.apache.ibatis.executor.BaseExecutor.flushStatements(BaseExecutor.java:127)at org.apache.ibatis.executor.BaseExecutor.flushStatements(BaseExecutor.java:120)at org.apache.ibatis.executor.BaseExecutor.commit(BaseExecutor.java:235)at org.apache.ibatis.executor.CachingExecutor.commit(CachingExecutor.java:112)at org.apache.ibatis.session.defaults.DefaultSqlSession.commit(DefaultSqlSession.java:196)at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:390)... 39 more

可以看到这种异常无法捕捉,仅能看到异常指向了druid和ibatis的原码处,初步猜测是由于默认的SqlSession无法支持这个数量级的批量操作,下面就结合源码和官方文档具体看一看。

源码分析

项目使用的是Spring+Mybatis,在Dao层是通过Spring提供的SqlSessionTemplate来获取SqlSession的:

@Resource(name = "sqlSessionTemplate")
private SqlSessionTemplate sqlSessionTemplate;public SqlSessionTemplate getSqlSessionTemplate()
{return sqlSessionTemplate;
}

为了验证,接下看一下它是如何提供SqlSesion的,打开SqlSessionTemplate的源码,看一下它的构造方法:

      /*** Constructs a Spring managed SqlSession with the {@code SqlSessionFactory}* provided as an argument.** @param sqlSessionFactory*/public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());}

接下来再点开getDefaultExecutorType这个方法:

  public ExecutorType getDefaultExecutorType() {return defaultExecutorType;}

可以看到它直接返回了类中的全局变量defaultExecutorType,我们再在类的头部寻找一下这个变量:

protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;

找到了,Spring为我们提供的默认执行器类型为Simple,它的类型一共有三种:

/*** @author Clinton Begin*/
public enum ExecutorType {SIMPLE, REUSE, BATCH
}

仔细观察一下,发现有3个枚举类型,其中有一个BATCH是否和批量操作有关呢?我们看一下mybatis官方文档中对这三个值的描述:
- ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。
- ExecutorType.REUSE: 这个执行器类型会复用预处理语句。
- ExecutorType.BATCH:这个执行器会批量执行所有更新语句,如果 SELECT 在它们中间执行还会标定它们是 必须的,来保证一个简单并易于理解的行为。

可以看到我的使用的SIMPLE会为每个语句创建一个新的预处理语句,也就是创建一个PreparedStatement对象,即便我们使用druid连接池进行处理,依然是每次都会向池中put一次并加入druid的cache中。这个效率可想而知,所以那个异常也有可能是insert timeout导致等待时间超过数据库驱动的最大等待值。


好了,已解决问题为主,根据分析我们选择通过BATCH的方式来创建SqlSession,官方也提供了一系列重载方法:

SqlSession openSession()
SqlSession openSession(boolean autoCommit)
SqlSession openSession(Connection connection)
SqlSession openSession(TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)

可以观察到主要有四种参数类型,分别是
- Connection connection
- ExecutorType execType
- TransactionIsolationLevel level
- boolean autoCommit

官方文档中对这些参数也有详细的解释:

SqlSessionFactory 有六个方法可以用来创建 SqlSession 实例。通常来说,如何决定是你 选择下面这些方法时:
Transaction (事务): 你想为 session 使用事务或者使用自动提交(通常意味着很多 数据库和/或 JDBC 驱动没有事务)?
Connection (连接): 你想 MyBatis 获得来自配置的数据源的连接还是提供你自己
Execution (执行): 你想 MyBatis 复用预处理语句和/或批量更新语句(包括插入和 删除)?

所以根据需求选择即可,由于我们要做的事情是批量insert,所以我们选择SqlSession openSession(ExecutorType execType, boolean autoCommit)

顺带一提关于TransactionIsolationLevel也就是我们经常提起的事务隔离级别,官方文档中也介绍的很到位:

MyBatis 为事务隔离级别调用使用一个 Java 枚举包装器, 称为 TransactionIsolationLevel,
否则它们按预期的方式来工作,并有 JDBC 支持的 5 级 (
NONE,
READ_UNCOMMITTED
READ_COMMITTED,
REPEATABLE_READ,
SERIALIZA BLE)

解决问题

回归正题,初步找到了问题原因,那我们换一中SqlSession的获取方式再试试看。


testing… 2minutes later…


不幸的是,依旧报相同的错误,看来不仅仅是ExecutorType的问题,那会不会是一次commit的数据量过大导致响应时间过长呢?上面我也提到了这种可能性,那么就再分批次处理试试,也就是说,在同一事务范围内,分批commit insert batch。具体看一下Dao层的代码实现:

    @Overridepublic boolean insertCrossEvaluation(List<CrossEvaluation> members)throws Exception {// TODO Auto-generated method stubint result = 1;SqlSession batchSqlSession = null;try {batchSqlSession = this.getSqlSessionTemplate().getSqlSessionFactory().openSession(ExecutorType.BATCH, false);// 获取批量方式的sqlsessionint batchCount = 1000;// 每批commit的个数int batchLastIndex = batchCount;// 每批最后一个的下标for (int index = 0; index < members.size();) {if (batchLastIndex >= members.size()) {batchLastIndex = members.size();result = result * batchSqlSession.insert("MutualEvaluationMapper.insertCrossEvaluation",members.subList(index, batchLastIndex));batchSqlSession.commit();System.out.println("index:" + index+ " batchLastIndex:" + batchLastIndex);break;// 数据插入完毕,退出循环} else {result = result * batchSqlSession.insert("MutualEvaluationMapper.insertCrossEvaluation",members.subList(index, batchLastIndex));batchSqlSession.commit();System.out.println("index:" + index+ " batchLastIndex:" + batchLastIndex);index = batchLastIndex;// 设置下一批下标batchLastIndex = index + (batchCount - 1);}}batchSqlSession.commit();} finally {batchSqlSession.close();}return Tools.getBoolean(result);}

再次测试,程序没有报异常,总共7728条数据 insert的时间大约为10s左右,如下图所示,

总结

简单记录一下Mybatis批量insert大数据量数据的解决方案,仅供参考,Tne End。

Mybatis 大数据量的批量insert解决方案相关推荐

  1. 大数据量下(批量)提升性能的方法以及常见的性能优化方法

    数据库层面(包含数据库设计和语句):尽量做到节省时间和数据库开销 1.让语句更加合理,符合查询优化的规则.避免全表扫描,建立高效索引,正确利用索引等. 2.避免频繁创建和删除临时表. 3.尽量避免向客 ...

  2. SQL Server中采用BULK INSERT实现大数据量文本文件批量导入

    SQL语句: BULK   INSERT   dbo.table        FROM   'e:/test.tbl '        WITH                (           ...

  3. .NET 大数据量并发解决方案

    .NET 大数据量并发解决方案 目录 .NET 大数据量并发解决方案 第一类情况 :大量的用户同时对系统的不同功能页面进行查找.更新操作 一.对服务器层面的处理 二.对数据库层面的处理 三.ADO.N ...

  4. 数据库大数据量、高并发、高可用解决方案!

    数据库性能瓶颈 对于一些互联网项目来说,企业为节省成本,一般会考虑将所有的数据都存储在一个数据库中,这个时候我们只需要考虑数据库优化.SQL优化.数据缓存.限流,消息队列.服务器性能等问题. 阿里巴巴 ...

  5. MyBatis Plus 解决大数据量查询慢问题

    分享知识 传递快乐 大数据量操作的场景大致如下: 数据迁移 数据导出 批量处理数据 在实际工作中当指定查询数据过大时,我们一般使用分页查询的方式一页一页的将数据放到内存处理.但有些情况不需要分页的方式 ...

  6. MYSQL数据库导入大数据量sql文件失败的解决方案

    最快办法:执行下方语句,单次有效. SET GLOBAL max_allowed_packet=30*1024*1024 如果想彻底更改,在安装mysql的文件夹下找到my.ini 文件,在文件末尾添 ...

  7. react大数据量渲染_React大量数据渲染的绝佳解决方案——React虚拟化组件

    介绍 在当下Web开发盛行的时代里,Web应用随之面临着各种问题,虽然前端框架或者前端解决方案百花齐放,但是并不是所有的问题都能很完美的解决.由于Web应用是基于浏览器渲染界面的,而由于浏览器瓶颈的限 ...

  8. echarts框架下大数据量展示的解决方案

    echarts+大数据量.这是个无解的问题! 大数据量,什么样的数据才算大呢?在echart 4.5.0版本中,画折线图,数据线一共1001000条,每条数据5002200个数据点,即最小数据5000 ...

  9. el-table 大数据量渲染,页面卡顿的解决方案

    原文: el-table大数据量渲染卡顿的解决方案 描述: 当el-table的数据有成千上万条,且在同一页全部展示,此时页面渲染的dom太多可能造成页面卡顿 原因: 因为数据量过多导致浏览器渲染过多 ...

最新文章

  1. Jenkins使用Publish Over FTP Plugin插件上传FTP详解
  2. Java多线程知识点整理(线程池)
  3. iOS UITest之加载其他应用
  4. FTP错误 [ftp: connect: No route to host] 解决方法
  5. 有了“手掌”,机械手也能盘“核桃”,耶鲁出品 | Science子刊
  6. OpenGL使用深度剥离命令获得独立的透明度
  7. linux SHELL脚本编程
  8. 《分布式系统:概念与设计》一2.3.2 体系结构模式
  9. hadoop2.2.0 分布式存储hdfs完全分布式搭建及功能测试记录(一)----架构及原理介绍...
  10. hadoop生态系统学习之路(十)MR将结果输出到hbase
  11. 蓝桥杯 ALGO-85 算法训练 进制转换
  12. matlab 求矩阵各行的平均值
  13. [转]网银支付接口编程资料汇总
  14. PhpStorm 2019 for mac(PHP集成开发工具) 2019.1.3中文激活版
  15. kmspico_setup.exe运行提示系统资源不足,无法完成请求的服务
  16. Android 中的WiFi学习笔记(转载)----WIFI启动 代码流程走读---网络连接流程
  17. 【VBA】Speech.Speak 指定语音库
  18. 判断浏览器类型及是否能使用ES6部分特性
  19. Allegro画不规则形状PCB
  20. 4G网络数据传输流程 三

热门文章

  1. 计算机十大硬件知识,电脑硬件怎么看好坏 史上最全DIY电脑硬件知识扫盲
  2. 数学建模模型——飞机巡航问题
  3. 半导体器件物理复习 NPN PNP等
  4. 理论联系实际,如何认识人民群众在历史发展中的作用
  5. 二维码生成细节和原理
  6. js 中控制打印方向横向还是纵向。
  7. 网络管理命令6— Pathping 命令
  8. 雷电模拟器安卓7以上+Charles抓包APP最新教程
  9. php 设置打印区域,CAD如何选择区域打印
  10. php jquery实例教程,jQuery效果实例分享