如今mysql普遍的插入方式有如下两种:

1、循环单条插入

  1.  
    <insert id="insert" parameterType="com.chargeProject.consumer.entity.Test">
  2.  
    insert into test (id, nums, name)
  3.  
    values (#{id,jdbcType=INTEGER}, #{nums,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR})
  4.  
    </insert>

2、拼装批量插入

  1.  
    <insert id="batchInsert" parameterType="java.util.List">
  2.  
    insert into test (id, nums, name)
  3.  
    values
  4.  
    <foreach collection="list" item="item" separator=",">
  5.  
    (#{item.id,jdbcType=INTEGER}, #{item.nums,jdbcType=INTEGER}, #{item.name,jdbcType=VARCHAR})
  6.  
    </foreach>
  7.  
    </insert>

一般都是通过mybatis框架进行辅助实现的,当然也可以自动拼装。今天介绍的是mysql自带的一种批量插入方式且效率更高,通过LOAD DATA LOCAL INFILE实现大批量插入。

MySQL使用LOAD DATA LOCAL INFILE从文件中导入数据比insert语句要快,MySQL文档上说要快20倍左右。
但是这个方法有个缺点,就是导入数据之前,必须要有文件,也就是说从文件中导入。这样就需要去写文件,以及文件删除等维护。某些情况下,比如数据源并发的话,还会出现写文件并发问题,很难处理。
那么有没有什么办法,可以达到同样的效率,直接从内存(IO流中)中导入数据,而不需要写文件呢?

MySQL社区提供这样一个方法:setLocalInfileInputStream(),此方法位于com.mysql.jdbc.PreparedStatement 类中。通过使用 MySQL JDBC 的setLocalInfileInputStream 方法实现从Java InputStream中load data local infile 到MySQL数据库中。

代码如下:

  1.  
    @Component
  2.  
    public class LoadDataInFileUtil {
  3.  
     
  4.  
    private Logger logger = LoggerFactory.getLogger(LoadDataInFileUtil.class);
  5.  
    private Connection conn = null;
  6.  
    @Resource
  7.  
    private JdbcTemplate jdbcTemplate;
  8.  
     
  9.  
    /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
  10.  
     
  11.  
    /**
  12.  
    * 将数据从输入流加载到MySQL。
  13.  
    *
  14.  
    * @param loadDataSql SQL语句。
  15.  
    * @param dataStream 输入流。
  16.  
    * @param jdbcTemplate JDBC。
  17.  
    * @return int 成功插入的行数。
  18.  
    */
  19.  
    private int bulkLoadFromInputStream(String loadDataSql,
  20.  
    InputStream dataStream,
  21.  
    JdbcTemplate jdbcTemplate) throws SQLException {
  22.  
    if (null == dataStream) {
  23.  
    logger.info("输入流为NULL,没有数据导入。");
  24.  
    return 0;
  25.  
    }
  26.  
    conn = jdbcTemplate.getDataSource().getConnection();
  27.  
    PreparedStatement statement = conn.prepareStatement(loadDataSql);
  28.  
    int result = 0;
  29.  
    if (statement.isWrapperFor(com.mysql.jdbc.Statement.class)) {
  30.  
    com.mysql.jdbc.PreparedStatement mysqlStatement = statement.unwrap(com.mysql.jdbc.PreparedStatement.class);
  31.  
    mysqlStatement.setLocalInfileInputStream(dataStream);
  32.  
    result = mysqlStatement.executeUpdate();
  33.  
    }
  34.  
    return result;
  35.  
    }
  36.  
     
  37.  
    /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
  38.  
     
  39.  
    /**
  40.  
    * 组装 SQL 语句。
  41.  
    *
  42.  
    * @param dataBaseName 数据库名。
  43.  
    * @param tableName 表名。
  44.  
    * @param columnName 要插入数据的列名。
  45.  
    */
  46.  
    public String assembleSql(String dataBaseName, String tableName, String columnName[]) {
  47.  
    String insertColumnName = StringUtils.join(columnName, ",");
  48.  
    String sql = "LOAD DATA LOCAL INFILE 'sql.csv' IGNORE INTO TABLE " + dataBaseName + "." + tableName + "(" + insertColumnName + ")";
  49.  
    return sql;
  50.  
    }
  51.  
     
  52.  
    /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
  53.  
     
  54.  
    /**
  55.  
    * 往 StringBuilder 里追加数据。
  56.  
    *
  57.  
    * @param builder StringBuilder。
  58.  
    * @param object 数据。
  59.  
    */
  60.  
    public void builderAppend(StringBuilder builder, Object object) {
  61.  
    builder.append(object);
  62.  
    builder.append("\t");
  63.  
    }
  64.  
     
  65.  
    /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
  66.  
     
  67.  
    /**
  68.  
    * 往 StringBuilder 里追加一条数据的最后一个字段。
  69.  
    *
  70.  
    * @param builder StringBuilder。
  71.  
    * @param object 数据。
  72.  
    */
  73.  
    public void builderEnd(StringBuilder builder, Object object) {
  74.  
    builder.append(object);
  75.  
    builder.append("\n");
  76.  
    }
  77.  
     
  78.  
    /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
  79.  
     
  80.  
    /**
  81.  
    * 通过 LOAD DATA LOCAL INFILE 大批量导入数据到 MySQL。
  82.  
    *
  83.  
    * @param sql SQL语句。
  84.  
    * @param builder 组装好的数据。
  85.  
    */
  86.  
    public int fastInsertData(String sql, StringBuilder builder) {
  87.  
    int rows = 0;
  88.  
    InputStream is = null;
  89.  
    try {
  90.  
    byte[] bytes = builder.toString().getBytes();
  91.  
    if (bytes.length > 0) {
  92.  
    is = new ByteArrayInputStream(bytes);
  93.  
    //批量插入数据。
  94.  
    long beginTime = System.currentTimeMillis();
  95.  
    rows = bulkLoadFromInputStream(sql, is, jdbcTemplate);
  96.  
    long endTime = System.currentTimeMillis();
  97.  
    logger.info("LOAD DATA LOCAL INFILE :【插入" + rows + "行数据至MySql中,耗时" + (endTime - beginTime) + "ms。】");
  98.  
    }
  99.  
     
  100.  
    } catch (SQLException e) {
  101.  
    e.printStackTrace();
  102.  
    } finally {
  103.  
    try {
  104.  
    if (null != is) {
  105.  
    is.close();
  106.  
    }
  107.  
    if (null != conn) {
  108.  
    conn.close();
  109.  
    }
  110.  
    } catch (IOException | SQLException e) {
  111.  
    e.printStackTrace();
  112.  
    }
  113.  
    }
  114.  
    return rows;
  115.  
    }
  116.  
     
  117.  
    /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
  118.  
     
  119.  
    }

调试代码如下:

  1.  
    // 数据库名。
  2.  
    public static final String DATA_BASE_NAME = "charge";
  3.  
    // 表名。
  4.  
    public static final String TABLE_NAME = "test";
  5.  
    // 要插入数据的列名。(必须与插入的数据一一对应)
  6.  
    public static final String COLUMN_NAME[] = {"id", "nums", "name"};
  7.  
     
  8.  
     
  9.  
     
  10.  
    @Override
  11.  
    public ResultContent insert(String name) {
  12.  
    StopWatch stopWatch = new StopWatch();
  13.  
    stopWatch.start();
  14.  
     
  15.  
    StringBuilder sb = new StringBuilder();
  16.  
    List<Test> list = new ArrayList<>();
  17.  
    for(int i = 1; i < 100000; i++) {
  18.  
    loadDataInFileUtil.builderAppend(sb, UUID.randomUUID().toString());
  19.  
    loadDataInFileUtil.builderAppend(sb, i);
  20.  
    loadDataInFileUtil.builderEnd(sb, name + i);
  21.  
    }
  22.  
     
  23.  
     
  24.  
    String sql = loadDataInFileUtil.assembleSql(DATA_BASE_NAME, TABLE_NAME, COLUMN_NAME);
  25.  
    int insertRow = loadDataInFileUtil.fastInsertData(sql, sb);
  26.  
    System.out.println("insert应收报表数量insertRow:"+insertRow);
  27.  
     
  28.  
     
  29.  
    stopWatch.stop();
  30.  
    System.out.println("花费时间" + stopWatch.getTotalTimeSeconds());
  31.  
     
  32.  
     
  33.  
     
  34.  
    System.out.println("---------方法执行结束--------------");
  35.  
    return new ResultContent(0, "success", name);
  36.  
    }

经过测试插入1w条数据时候与拼装批量插入语句时间差别不大,当插入数量达到10w出现了明显的时间差:

拼装批量插入语句花费时间:6.83s

LOAD DATA LOCAL INFILE实现大批量插入花费时间:1.23s

当表格的字段更多数据量更大出现的时间差就越大。

总结:当需要进行大批量数据插入的时候,可以优先考虑LOAD DATA LOCAL INFILE实现方式。

MySQL优化之LOAD DATA LOCAL INFILE实现大批量插入的更多相关文章

  1. 使用 LOAD DATA LOCAL INFILE,sysbench 导数速度提升30%

    1. LOAD DATA INFILE 为什么比 INSERT 快? 2. sysbench 压测 MySQL 的四个标准步骤. 3. 怎么让 sysbench 支持 LOAD DATA LOCAL ...

  2. [MySQL]load data local infile向MySQL数据库中导入数据时,无法导入和字段不分离问题。

    利用load data将文件中的数据导入数据库表中的时候,遇到了两个问题. 首先是load data命令无法执行的问题: 命令行下输入load data local infile "path ...

  3. MySQL使用LOAD DATA LOCAL INFILE报错

    在windows系统的MySQL8.0中尝试执行以下语句时报错 mysql> LOAD DATA LOCAL INFILE '/path/filename' INTO TABLE tablena ...

  4. 浅谈MySQL load data local infile细节 -- 从源码层面

    相信大伙对mysql的load data local infile并不陌生,今天来巩固一下这里面隐藏的一些细节,对于想自己动手开发一个mysql客户端有哪些点需要注意的呢? 首先,了解一下流程: 3个 ...

  5. MySQL用Load Data local infile 导入部分数据后中文乱码

    今天在两台MySQL服务器之间导数据,因为另一个MySQL服务器是测试用的,差一个月的数据,从现有MySQL服务器select到一个文件,具体语句是: select * from news where ...

  6. Java不写文件,LOAD DATA LOCAL INFILE大批量导入数据到MySQL的实现(转)

    MySQL使用load data local infile 从文件中导入数据比insert语句要快,MySQL文档上说要快20倍左右.但是这个方法有个缺点,就是导入数据之前,必须要有文件,也就是说从文 ...

  7. Load data local infile 实验报告

    1.实验内容: 利用SQL语句“load data local infile”将“pet.txt”文本文件中的数据导入到mysql中 (pet表在数据库menagerie中) 2.实验过程及结果: ( ...

  8. load data local infile

    发财 基本语法:load data [low_priority] [local] infile '文件名称' [replace替换策略 | ignore忽略策略]into table 表名称[fiel ...

  9. 合理使用mysql中的load data infile导入数据

    基本语法: load data  [low_priority] [local] infile 'file_name txt' [replace | ignore]into table tbl_name ...

  10. Mybatis拦截器 mysql load data local 内存流处理

    Mybatis 拦截器不做解释了,用过的基本都知道,这里用load data local主要是应对大批量数据的处理,提高性能,也支持事务回滚,且不影响其他的DML操作,当然这个操作不要涉及到当前所lo ...

随机推荐

  1. JPA在SpringBoot中简单使用

    前言 在SpringBoot项目中可以与JPA进行搭配,这样会省很多的开发时间,以下为JPA的简单使用 一.导入依赖 <!-- springboot jpa依赖--> <depend ...

  2. 记一次 .NET 某发证机系统 崩溃分析

    一:背景 1. 讲故事 前些天有位朋友在微信上找到我,说他的系统有偶发崩溃,自己也没找到原因,让我帮忙看下怎么回事,我分析dump一直都是免费的,毕竟对这些东西挺感兴趣,有问题可以直接call我,好了 ...

  3. 非常'肤浅'的理解MVVM

    那天领导给了我这么一个需求,就是他会通过接口给我传递一条数据,然后我需要判断这条数据的首字母是不是A,如果是的话,就把这条数据保存下来 很简单的一个需求对吧,直接开干,代码如下 完美的解决这个问题,所 ...

  4. HZOI NOIP 2024 Round 24 T2 取石子 官方做法

    发现大多数的题解都是不同于官方题解的做法,这里我将介绍官方题解做法. Solution 证明先手是否可以必胜的方法相差无几,为了方便后边行文,这里介绍我的思路:考虑各堆石子和为奇数的情况(以下简称为& ...

  5. 从 TCP 到 WebSocket:一次搞懂网络通信的三层演进

    引言 在现代应用开发中,网络通信是绕不开的核心议题.无论是构建传统的 Web 应用,还是开发需要实时交互的系统(如在线协作工具.金融行情推送.多人游戏),我们总会与 TCP.HTTP.WebSocke ...

  6. DotTrace系列:9. 大结局之 跨平台 和 自定义行为 诊断

    一:背景 1. 讲故事 本篇是系列的最后一篇,我们从跨平台部署和自定义诊断的角度跟大家聊一聊 dottrace,希望对大家有所启发. 二:跨平台和自定义诊断 1. 如何跨平台诊断分析 如果 dottr ...

  7. Kafka入门实战教程(6):调优Kafka的实践

    1 调优Kafka的目标 通常来说,任何系统调优的目标都是为了满足系统常见的非功能性需求,而性能则是众多非功能性需求中最重要的一个. 不同的系统对性能的侧重点不同,DB的话性能是响应时间,而对于Kaf ...

  8. org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connec

    (springcloud Stream整合rabbitmq消息驱动生产者踩坑)消息驱动之生产者8801 1.首先说一下情况,我是跟着尚硅谷周阳老师的springcloud2020教程学习的,前面也踩了 ...

  9. English-英语发音舌位唇形图

    英语发音舌位唇形图 图0 梯形图 图1 图2 图3

  10. Win11系统电脑如何取消登录密码的问题

    很多电脑基地的用户升级win11系统,但是电脑设置了密码之后,每次开机需要输入密码才能进入桌面.其实,我们设置密码是为了防止陌生人开机就可以进入桌面,但是自己每次进入桌面也要输入密码就很麻烦,下面技术 ...