mysql批量插入大量数据「建议收藏」

mysql批量插入大量数据「建议收藏」mysql批量插入大量数据时间:2020年11月25日今天遇到了一个批量插入大量数据任务,然后出于小白本能,直接for-each循环插入不就好了,于是手上开始噼里啪啦一顿操作,写好了从读取excel到插入数据库的工作,于是就美滋滋的开始了自己的测试,试了一把,一次通过perfect,然后后面就悲剧了,后面发现数据量稍微大一点,速度就会很慢很慢。于是掏出自己的制胜法典,后来我在知识和海洋中获取到了两种靠谱的方法。下面一点一点讲。测试的服务器信息1核2g5m的阿里云服务器(你懂得),mysql直接装


Jetbrains全系列IDE稳定放心使用

mysql批量插入大量数据

时间:2020年11月25日

今天遇到了一个批量插入大量数据任务,然后出于小白本能,直接for-each循环插入不就好了,于是手上开始噼里啪啦一顿操作,写好了从读取excel到插入数据库的工作,于是就美滋滋的开始了自己的测试,试了一把,一次通过perfect,然后后面就悲剧了,后面发现数据量稍微大一点,速度就会很慢很慢。于是掏出自己的制胜法典,后来我在知识和海洋中获取到了两种靠谱的方法。下面一点一点讲。

测试的服务器信息 1核2g 5m的阿里云服务器(你懂得),mysql直接装在服务器本机,没有通过docker安装,每次测试之前会通过代码将表截断

一、method-1

原始的也是最笨的方法

 @RequestMapping(value = "/test1", method = RequestMethod.GET)
    public String test1() { 
   

        ArrayList<TestTest> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) { 
   
            TestTest testTest = new TestTest();
            testTest.setField1("setField1" + i);
            testTest.setField2("setField2" + i);
            testTest.setField3("setField3" + i);
            testTest.setField4("setField4" + i);
            list.add(testTest);
        }

        //执行前截断表,保证每次测试环境的一致性
        testTestMapper.trunCate();

        Long startTime = System.currentTimeMillis();
        list.stream().forEach(test -> { 
   
            testTestMapper.insert(test);
        });
        Long endTime = System.currentTimeMillis();
        System.out.println("OK 耗时:" + (endTime - startTime) + "毫秒");
        return "OK 耗时:" + (endTime - startTime) + "毫秒";
    }

这个速度真真的慢的离谱,所以我只插入1000条给大家看一下效果就好了

在这里插入图片描述

这个结果是不是就很离谱。。

二、method-2

用mybatis的方法,拼接插入参数,一次性插入

@RequestMapping(value = "/test2", method = RequestMethod.GET)
    public String test2() { 
   

        ArrayList<TestTest> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) { 
   
            TestTest testTest = new TestTest();
            testTest.setField1("setField1" + i);
            testTest.setField2("setField2" + i);
            testTest.setField3("setField3" + i);
            testTest.setField4("setField4" + i);
            list.add(testTest);
        }
        //执行前截断表,保证每次测试环境的一致性
        testTestMapper.trunCate();

        Long startTime = System.currentTimeMillis();
        testTestMapper.insertBatch(list);
        Long endTime = System.currentTimeMillis();
        System.out.println("OK 耗时:" + (endTime - startTime) + "毫秒");
        return "OK 耗时:" + (endTime - startTime) + "毫秒";
    }
    <!--批量新增所有列,列表长度不能为0,且列表id统一为null或者统一不为null-->
    <insert id="insertBatch" keyProperty="id" useGeneratedKeys="true" parameterType="list">
        insert into test_test
         (field1, field2, field3, field4)
        values
        <foreach item="item" collection="list" separator="," open="" close="" index="index">
         (<if test="item.id != null">#{item.id,jdbcType=INTEGER},</if>#{item.field1,jdbcType=VARCHAR}, #{item.field2,jdbcType=VARCHAR}, #{item.field3,jdbcType=VARCHAR}, #{item.field4,jdbcType=VARCHAR})
        </foreach>
    </insert>

这个时候我们插入1w条数据进行比较
在这里插入图片描述

1w条数据插入了11s,比上面不知道快了多少,可是这样插入是有一个弊端的,就是数据量再大一点的话,会报错的,我改成10w去跑一下给你们看一下效果

### Cause: com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (9,455,639 > 4,194,304). You can change this value on the server by setting the 'max_allowed_packet' variable.
; Packet for query is too large (9,455,639 > 4,194,304). You can change this value on the server by setting the 'max_allowed_packet' variable.; nested exception is com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (9,455,639 > 4,194,304). You can change this value on the server by setting the 'max_allowed_packet' variable.] with root cause

com.mysql.cj.jdbc.exceptions.PacketTooBigException: Packet for query is too large (9,455,639 > 4,194,304). You can change this value on the server by setting the 'max_allowed_packet' variable.
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:107)
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:970)
	at com.mysql.cj.jdbc.ClientPreparedStatement.execute(ClientPreparedStatement.java:387)
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3409)
	at com.alibaba.druid.filter.FilterEventAdapter.preparedStatement_execute(FilterEventAdapter.java:440)
	at com.alibaba.druid.filter.FilterChainImpl.preparedStatement_execute(FilterChainImpl.java:3407)
	at com.alibaba.druid.proxy.jdbc.PreparedStatementProxyImpl.execute(PreparedStatementProxyImpl.java:167)
	at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:498)
	at org.apache.ibatis.executor.statement.PreparedStatementHandler.update(PreparedStatementHandler.java:47)
	at org.apache.ibatis.executor.statement.RoutingStatementHandler.update(RoutingStatementHandler.java:74)
	at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:50)
	at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:117)
	at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:76)

这是因为在对mysql进行插入、更新或查询操作时,mysql server接收处理的数据包大小是有限制的,如果太大超过了设置的max_allowed_packet参数的大小,会导致操作失败,我们可以通过命令:show VARIABLES like ‘%max_allowed_packet%’;查看参数值。你也可以去修改mysql的配置文件去解决,但是生产上有时候自己身不由己,所以求人不如求自己,自己再想想办法。

三、method-3

第三种,通过原生的jdbc连接设置,然后打开批量处理的方式去处理数据

MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。
MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL
另外这个选项对INSERT/UPDATE/DELETE都有效。

url上必须加上rewriteBatchedStatements=true

private String url = "jdbc:mysql://39.97.103.5:3306/study?rewriteBatchedStatements=true&characterEncoding=utf-8&serverTimezone=UTC";
private String user = "root";
private String password = "password";
    
 @RequestMapping(value = "/test3", method = RequestMethod.GET)
    public String test3() { 
   

        ArrayList<TestTest> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) { 
   
            TestTest testTest = new TestTest();
            testTest.setField1("setField1" + i);
            testTest.setField2("setField2" + i);
            testTest.setField3("setField3" + i);
            testTest.setField4("setField4" + i);
            list.add(testTest);
        }

        //执行前截断表,保证每次测试环境的一致性
        testTestMapper.trunCate();

        Long startTime = System.currentTimeMillis();
        batctMysql(list);
        Long endTime = System.currentTimeMillis();
        System.out.println("OK 耗时:" + (endTime - startTime) + "毫秒");
        return "OK 耗时:" + (endTime - startTime) + "毫秒";
    }

    public void batctMysql(ArrayList<TestTest> list) { 
   
        Connection conn = null;
        PreparedStatement pstm = null;
        try { 
   
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection(url, user, password);
            //关闭事务自动提交
            conn.setAutoCommit(false);


            String sql = "INSERT INTO test_test (field1,field2,field3,field4) VALUES(?,?,?,?)";
            pstm = conn.prepareStatement(sql);

            for (TestTest test : list) { 
   
                pstm.setString(1, test.getField1());
                pstm.setString(2, test.getField2());
                pstm.setString(3, test.getField3());
                pstm.setString(4, test.getField4());
                pstm.addBatch();
            }
            //批处理
            pstm.executeBatch();
            //提交事务
            conn.commit();
        } catch (Exception e) { 
   
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally { 
   
            if (pstm != null) { 
   
                try { 
   
                    pstm.close();
                } catch (SQLException e) { 
   
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
            if (conn != null) { 
   
                try { 
   
                    conn.close();
                } catch (SQLException e) { 
   
                    e.printStackTrace();
                    throw new RuntimeException(e);
                }
            }
        }

    }
    

测试结果

在这里插入图片描述

1w条数据只用了6s多,处理速度还是最快的一种
测试一下10w条记录的时间

在这里插入图片描述

系统没有报错,然后时间还可以接受

四、总结

各位铁子们,千万不要使用第一种方式去处理数据,这样你会糟重的,小心点。至于第二种和第三种方式,如果自己数据量不大的话,也可考虑第二种。但是第三种的效率是真强。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/185548.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 在Pycharm的环境下安装OpenCV「建议收藏」

    在Pycharm的环境下安装OpenCV「建议收藏」目录前言PythonPyCharmPython下安装OpenCvPycharm安装opencv-python测试小程序前言因为自己打算开始学图像视觉的知识,所以,就从OpenCv开始学起,没想到,一开始就被这个装环境搞得有点烦了,这里做一个记录,我觉得还是跟网上现在随便搜下来的一些教程不同的,特别是我会教你怎么比较快速的安装一些东西,不然,你应该是会觉得很烦的,比较要安装OpenCv的很多库都是在国外,所以下载速度很慢,好的,现在我们正式开始。Python首先,肯定是Python的下载和安装了,我知

    2022年8月28日
  • Java 关于重写compareTo方法

    Java 关于重写compareTo方法概述”当一个类实现类Comparable接口,此类就可以跟很多泛型算法(genericalgorithm)以及依赖于该接口的集合实现(Collectionimplementation)进行协作”比如:字母排序,按数字排序,年代排序等等某种**定制排序**Comparable接口publicinterfaceComparable{intcompareTo(Tt);}int

    2022年7月13日
  • 离散傅里叶变换公式推导

    离散傅里叶变换公式推导离散傅里叶变换公式推导先抛变换公式:Fm=∑n=0N−1fne−2πimn/N↔fn=1N∑m=0N−1Fme2πimn/NF_m=\sum_{n=0}^{N-1}f_ne^{-2\piimn/N}\leftrightarrowf_n=\frac{1}{N}\sum_{m=0}^{N-1}F_me^{2\piimn/N}Fm​=n=0∑N−1​fn​e−2πimn/N↔fn​=N1​m=0∑N−1​Fm​e2πimn/N式中的N是数据点个数讲道理一开始完全看不懂公式这么来的,一顿百度后我学

    2022年7月17日
  • journalctl命令详解,与如何查看系统日志

    journalctl命令详解,与如何查看系统日志journalctl可以查看所有的系统日志文件,由于日志信息量很大,journalctl还提供了各种参数帮助用户更快速的定位到日志信息。默认情况下,用户都可以访问自己的日志。对于系统主日志和其他用户的日志,仅限于有权限的用户访问,比如root用户,wheel组和systemd组的用户。

    2022年5月10日
  • sql server 字符串转日期_sql server 字符串替换

    sql server 字符串转日期_sql server 字符串替换提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档sqlserver字符串转为日期时间格式使用步骤1.引入库2.读入数据总结使用步骤1.引入库代码如下(示例):importnumpyasnpimportpandasaspdimportmatplotlib.pyplotaspltimportseabornassnsimportwarningswarnings.filterwarnings(‘ignore’)importsslssl._c

    2022年10月8日
  • Visual Studio 2017 – Windows应用程序打包成exe文件(2)- Advanced Installer 关于Newtonsoft.Json,LINQ to JSON的一个小d…

    Visual Studio 2017 – Windows应用程序打包成exe文件(2)- Advanced Installer 关于Newtonsoft.Json,LINQ to JSON的一个小d…

    2021年6月9日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号