实现数据批量插入(jdbc\mybatis)

1. 实现方式 – JDBC:

  • 普通方式插入for循环
 1    private String url = "jdbc:mysql://localhost:3306/test01";2     private String user = "root";3     private String password = "123456";4     @Test5     public void Test(){6         Connection conn = null;7         PreparedStatement pstm =null;8         ResultSet rt = null;9         try {
10             Class.forName("com.mysql.jdbc.Driver");
11             conn = DriverManager.getConnection(url, user, password);
12             String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13             pstm = conn.prepareStatement(sql);
14             Long startTime = System.currentTimeMillis();
15             Random rand = new Random();
16             int a,b,c,d;
17             for (int i = 1; i <= 1000; i++) {
18                     pstm.setInt(1, i);
19                     pstm.setInt(2, i);
20                     a = rand.nextInt(10);
21                     b = rand.nextInt(10);
22                     c = rand.nextInt(10);
23                     d = rand.nextInt(10);
24                     pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
25                     pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);27                     pstm.executeUpdate();
28             }
29             Long endTime = System.currentTimeMillis();
30             System.out.println("OK,用时:" + (endTime - startTime));
31         } catch (Exception e) {
32             e.printStackTrace();
33             throw new RuntimeException(e);
34         }finally{
35             if(pstm!=null){
36                 try {
37                     pstm.close();
38                 } catch (SQLException e) {
39                     e.printStackTrace();
40                     throw new RuntimeException(e);
41                 }
42             }
43             if(conn!=null){
44                 try {
45                     conn.close();
46                 } catch (SQLException e) {
47                     e.printStackTrace();
48                     throw new RuntimeException(e);
49                 }
50             }
51         }
52     }
  • 使用事务方式插入
    先将命令的提交方式设为false,即手动提交conn.setAutoCommit(false);最后在所有命令执行完之后再提交事务conn.commit();
 private String url = "jdbc:mysql://localhost:3306/test01";2     private String user = "root";3     private String password = "123456";4     @Test5     public void Test(){6         Connection conn = null;7         PreparedStatement pstm =null;8         ResultSet rt = null;9         try {
10             Class.forName("com.mysql.jdbc.Driver");
11             conn = DriverManager.getConnection(url, user, password);
12             String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13             pstm = conn.prepareStatement(sql);
14             conn.setAutoCommit(false);
15             Long startTime = System.currentTimeMillis();
16             Random rand = new Random();
17             int a,b,c,d;
18             for (int i = 1; i <= 100000; i++) {
19                     pstm.setInt(1, i);
20                     pstm.setInt(2, i);
21                     a = rand.nextInt(10);
22                     b = rand.nextInt(10);
23                     c = rand.nextInt(10);
24                     d = rand.nextInt(10);
25                     pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
26                     pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);
27                     pstm.executeUpdate();
28             }
29             conn.commit();
30             Long endTime = System.currentTimeMillis();
31             System.out.println("OK,用时:" + (endTime - startTime));
32         } catch (Exception e) {
33             e.printStackTrace();
34             throw new RuntimeException(e);
35         }finally{
36             if(pstm!=null){
37                 try {
38                     pstm.close();
39                 } catch (SQLException e) {
40                     e.printStackTrace();
41                     throw new RuntimeException(e);
42                 }
43             }
44             if(conn!=null){
45                 try {
46                     conn.close();
47                 } catch (SQLException e) {
48                     e.printStackTrace();
49                     throw new RuntimeException(e);
50                 }
51             }
52         }
53     }
  • 批量处理
    首先,JDBC连接的url中要加rewriteBatchedStatements参数设为true是批量操作的前提,其次就是检查mysql驱动包时候是5.1.13以上版本(低于该版本不支持)
private String url = "jdbc:mysql://localhost:3306/test01?rewriteBatchedStatements=true";2     private String user = "root";3     private String password = "123456";4     @Test5     public void Test(){6         Connection conn = null;7         PreparedStatement pstm =null;8         ResultSet rt = null;9         try {
10             Class.forName("com.mysql.jdbc.Driver");
11             conn = DriverManager.getConnection(url, user, password);
12             String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13             pstm = conn.prepareStatement(sql);
14             Long startTime = System.currentTimeMillis();
15             Random rand = new Random();
16             int a,b,c,d;
17             for (int i = 1; i <= 100000; i++) {
18                     pstm.setInt(1, i);
19                     pstm.setInt(2, i);
20                     a = rand.nextInt(10);
21                     b = rand.nextInt(10);
22                     c = rand.nextInt(10);
23                     d = rand.nextInt(10);
24                     pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
25                     pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);
26                     pstm.addBatch();
27             }
28             pstm.executeBatch();
29             Long endTime = System.currentTimeMillis();
30             System.out.println("OK,用时:" + (endTime - startTime));
31         } catch (Exception e) {
32             e.printStackTrace();
33             throw new RuntimeException(e);
34         }finally{
35             if(pstm!=null){
36                 try {
37                     pstm.close();
38                 } catch (SQLException e) {
39                     e.printStackTrace();
40                     throw new RuntimeException(e);
41                 }
42             }
43             if(conn!=null){
44                 try {
45                     conn.close();
46                 } catch (SQLException e) {
47                     e.printStackTrace();
48                     throw new RuntimeException(e);
49                 }
50             }
51         }
52     }
  • 事务操作+批量处理方式
 private String url = "jdbc:mysql://localhost:3306/test01?rewriteBatchedStatements=true";2     private String user = "root";3     private String password = "123456";4     @Test5     public void Test(){6         Connection conn = null;7         PreparedStatement pstm =null;8         ResultSet rt = null;9         try {
10             Class.forName("com.mysql.jdbc.Driver");
11             conn = DriverManager.getConnection(url, user, password);
12             String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13             pstm = conn.prepareStatement(sql);
14             conn.setAutoCommit(false);
15             Long startTime = System.currentTimeMillis();
16             Random rand = new Random();
17             int a,b,c,d;
18             for (int i = 1; i <= 100000; i++) {
19                     pstm.setInt(1, i);
20                     pstm.setInt(2, i);
21                     a = rand.nextInt(10);
22                     b = rand.nextInt(10);
23                     c = rand.nextInt(10);
24                     d = rand.nextInt(10);
25                     pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
26                     pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);
27                     pstm.addBatch();
28             }
29             pstm.executeBatch();
30             conn.commit();
31             Long endTime = System.currentTimeMillis();
32             System.out.println("OK,用时:" + (endTime - startTime));
33         } catch (Exception e) {
34             e.printStackTrace();
35             throw new RuntimeException(e);
36         }finally{
37             if(pstm!=null){
38                 try {
39                     pstm.close();
40                 } catch (SQLException e) {
41                     e.printStackTrace();
42                     throw new RuntimeException(e);
43                 }
44             }
45             if(conn!=null){
46                 try {
47                     conn.close();
48                 } catch (SQLException e) {
49                     e.printStackTrace();
50                     throw new RuntimeException(e);
51                 }
52             }
53         }
54     }

2 . 实现方式 – mybatis
mybatis中的foreach循环

<insert id="batchInsert" parameterType="java.util.List">insert into USER (id, name) values<foreach collection="list" item="model" index="index" separator=","> (#{model.id}, #{model.name})</foreach>
</insert>

其原理是将传统的单条insert语句

INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");

转换为:

INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2"),("data1", "data2"),("data1", "data2"),("data1", "data2"),("data1", "data2");

理想情况下,这样可以在单个连接中一次性发送许多新行的数据,并将所有索引更新和一致性检查延迟到最后才进行。
乍看上去这个foreach没有问题,但是经过项目实践发现,当表的列数较多(20+),以及一次性插入的行数较多(5000+)时,整个插入的耗时十分漫长,达到了14分钟,这是不能忍的。
mybatis默认执行器类型为Simple,会为每个语句创建一个新的预处理语句,也就是创建一个PreparedStatement对象。在我们的项目中,会不停地使用批量插入这个方法,而因为MyBatis对于含有的语句,无法采用缓存,那么在每次调用方法时,都会重新解析sql语句。
所以,如果非要使用 foreach 的方式来进行批量插入的话,可以考虑减少一条 insert 语句中 values 的个数,最好能达到上面曲线的最底部的值,使速度最快。一般按经验来说,一次性插20~50行数量是比较合适的,时间消耗也能接受。
重点来了。上面讲的是,如果非要用的方式来插入,可以提升性能的方式。而实际上,MyBatis文档中写批量插入的时候,是推荐使用另外一种方法。(可以看 http://www.mybatis.org/mybatis-dynamic-sql/docs/insert.html 中 Batch Insert Support 标题里的内容)


SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {SimpleTableMapper mapper = session.getMapper(SimpleTableMapper.class);List<SimpleTableRecord> records = getRecordsToInsert(); // not shownBatchInsert<SimpleTableRecord> batchInsert = insert(records).into(simpleTable).map(id).toProperty("id").map(firstName).toProperty("firstName").map(lastName).toProperty("lastName").map(birthDate).toProperty("birthDate").map(employed).toProperty("employed").map(occupation).toProperty("occupation").build().render(RenderingStrategy.MYBATIS3);batchInsert.insertStatements().stream().forEach(mapper::insert);session.commit();
} finally {session.close();
}

即基本思想是将 MyBatis session 的 executor type 设为 Batch ,然后多次执行插入语句。就类似于JDBC的下面语句一样。


Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb?useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=false&rewriteBatchedStatements=true","root","root");
connection.setAutoCommit(false);
PreparedStatement ps = connection.prepareStatement("insert into tb_user (name) values(?)");
for (int i = 0; i < stuNum; i++) {ps.setString(1,name);ps.addBatch();
}
ps.executeBatch();
connection.commit();
connection.close();

经过试验,使用了 ExecutorType.BATCH 的插入方式,性能显著提升,不到 2s 便能全部插入完成。

总结一下,如果MyBatis需要进行批量插入,推荐使用 ExecutorType.BATCH 的插入方式,如果非要使用 的插入的话,需要将每次插入的记录控制在 20~50 左右。

3. 方案
因为要考虑JVM的GC所以数据应该限制一下,但鉴于Mybatis大数据量的批量插入效率不高,所以根据数据大小分段治理。

3.1 小于1W使用:Mybatis批量插入方案
对JVM进行调优,但主要的性能瓶颈在批量插入操作。鉴于mybatis在项目开发方面的优势,数据量很小的情况下还是建议使用Mybatis。

3.2 大于1W小于10W使用:JDBC批量+事务处理
对JVM进行调优(设置Stack和GC等)。一般操作30秒以内是可以容忍的性能耗时。

3.3 10W以上数据使用:数据分批+JDBC批量+事务处理
对JVM进行调优(设置Stack和GC等),通过数据分批处理。对于分批处理需要借鉴前面的测试数据来定义分批量的大小,主要是对操作时间调优。

mybatis批量导入相关推荐

  1. java list分批_Java实用笔记——mybatis批量导入

    1.使用Statement 批量插入 public void batchInsertJdbc1() throws Exception {DataSource ds = (DataSource) Spr ...

  2. vue表单中批量导入功能_spring boot mybatis+ vue 使用POI实现从Excel中批量导入数据

    一.前端vue+element 1.前端使用element的upload组件来实现文件的上传 style="display: inline-flex;margin-right: 8px&qu ...

  3. 【Springboot+mybatis】 解析Excel并批量导入到数据库

    [Springboot+mybatis] 解析Excel并批量导入到数据库 置顶 2018年01月16日 20:05:52 冉野丶 阅读数:4060 标签: excel导入数据库 文件上传 excel ...

  4. 关联表多数据的批量insert (批量导入,测试19W条数据用时46秒)

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 一.业务需求 :作多个批量导入 ,根据业务不同,每条数据导入前作各种验证,             ...

  5. Mybatis框架 导入/导出功能的实现

    导出功能: 1.dao层的mapper(写方法名) 2.xml里用sql语句实现mapper中的方法  注意: 映射时对比数据库段名和bean变量名 映射的property和sql语句需要查询的所有变 ...

  6. Java:实现文件批量导入导出实践(兼容xls,xlsx)

    点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 作者:小卖铺的老爷爷 cnblogs.com/laoyeye/p/6938889.html ...

  7. XX健康:预约管理-预约设置日历插件文件简单下载Excel文件解析Excel表数据批量导入

    1. 需求分析 前面我们已经完成了检查项管理.检查组管理.套餐管理等.接下来我们需要进行预约设置,其实就是设置每一天的体检预约最大数量.客户可以通过微信端在线预约,在线预约时需要选择体检的时间,如果客 ...

  8. springboot 导入excel(数据批量导入)

    springboot excel数据批量导入 1.pom.xml 引入poi依赖 <!--poi--><dependency><groupId>org.apache ...

  9. 通过jsp向mysql批量导入数据_对大数据的批量导入MySQL数据库

    自己的库里有索引在用insert导入数据时会变慢很多 使用事务+批量导入 可以配置使用spring+mybatis整合的方式关闭自动提交事务(地址),选择批量导入每一百条导入使用list存储值传入到m ...

  10. java使用POI实现Excel批量导入数据。

    1.背景 项目中有使用easypoi,处理常规excel问题,但是现在有个需求,需要动态生成导出的报表字段.同时,根据导入的excel,增加数据信息.(有可能会出现,导入的报表是几天前下载的,不会最新 ...

最新文章

  1. 助力AI腾飞,深度学习走向何方?
  2. Kubernetes 最佳安全实践指南
  3. tomcat如何增大并发_Tomcat 7最大并发连接数的正确修改方法
  4. Red5 简单安装指南
  5. 生成jar文件的方法
  6. 计算机网络——速率相关的性能指标
  7. ldap和kerberos整合大数据账号
  8. ...is public, should be declared in a file named “ScresourcesApplic.java“---springcloud工作笔记164
  9. Google是否投资你,得看AI支持不支持
  10. PHP 数据库中的模糊查询
  11. JSTL使用总结(2) fmt标签库和fn标签库
  12. Xcode 模拟器复制解决方案
  13. Hibernate Annotation 设置字段的默认值
  14. java面试题1 牛客:A派生出子类B,B派生出子类C,并且在java源代码中有如下声明:
  15. SpringBoot实现文件在线预览
  16. 单基因gsea_单基因突变+肿瘤突变负荷+免疫细胞浸润文章套路
  17. Dextran-PEG2000-Conjugate,葡聚糖聚乙二醇Conjugate,葡聚糖聚乙二醇,属于葡聚糖缀合物
  18. JS gkb转utf8(fetch gbk网页是乱码)
  19. 【AD22】设置原理图纸张大小
  20. LS,MMSE,LMMSE,ML,MAP,LMS,AR,MSE误差介绍

热门文章

  1. c语言头文件malloc.h,#includemalloc.h,sys/types.h ,stdlib.h,string.h及C语言头文件
  2. 计算机考研复试难,艰难与快乐:2008年重庆邮电大学计算机考研复试经历
  3. 安卓软件改名器_安卓歌词适配V3.9.5 无损音乐下载器(软件篇)
  4. 三星980 NVMe SSD推出:首款不含 DRAM 的消费类硬盘
  5. yandex安装插件教程,研究了一下午终于可以用了
  6. 小米盒子共享电脑文件 服务器,小米盒子3S访问局域网电脑共享安装软件和观看视频的方法...
  7. 青岛自然人税收管理系统服务器地址,青岛市自然人税收管理系统扣缴客户端
  8. Kafka的常用命令(包括:下载安装、后台启动)
  9. linux命令行大全第2版,Linux命令行大全(第2版)
  10. 【效率工具合集】Win10镜像迅雷快速安装