Mybatis批量插入

2021-11-19  本文已影响0人  justlinzhihe

测试表结构
create table batch_test ( id int auto_increment primary key, username varchar(20) not null, password varchar(100) not null, address varchar(100) null );
随机生成10w条数据

    private List<BatchTestDO> generateData() {
        List<BatchTestDO> list = new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            BatchTestDO entity = new BatchTestDO();
            entity.setUsername("小明" + i);
            entity.setPassword(UUID.randomUUID().toString());
            entity.setAddress(UUID.randomUUID().toString());
            list.add(entity);
        }
        return list;
    }

1.mybatis的BatchExecutor
数据库连接配置:url: jdbc:mysql://127.0.0.1:3306/test?rewriteBatchedStatements=true,主要就是rewriteBatchedStatements这个参数

    @Test
    public void test001() throws Exception {
        List<BatchTestDO> data = generateData();
        long start=System.currentTimeMillis();
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
        batchTestMapper=session.getMapper(BatchTestMapper.class);
        for (BatchTestDO datum : data) {
            batchTestMapper.insert(datum);
        }
        session.commit();
        System.out.println(String.format("用时:%d",System.currentTimeMillis()-start));

    }
image.png

十万数据插入4634ms
2.用jdbc驱动原始的批量提交方式

    @Test
    public void test002() throws SQLException {
        List<BatchTestDO> data = generateData();
        Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test?rewriteBatchedStatements=true","root","123456abc");
        long start=System.currentTimeMillis();
        connection.setAutoCommit(false);
        PreparedStatement ps = connection.prepareStatement(
                "insert into batch_test (username,password,address) values(?,?,?)");
        for (int i = 0; i < data.size(); i++) {
            BatchTestDO batchTestDO=data.get(i);
            ps.setString(1,batchTestDO.getUsername());
            ps.setString(2,batchTestDO.getPassword());
            ps.setString(3,batchTestDO.getAddress());
            ps.addBatch();
        }
        ps.executeBatch();
        connection.commit();
        System.out.println(String.format("用时:%d",System.currentTimeMillis()-start));
        connection.close();
    }
image.png

只用了2486ms
3.用foreach标签的方式

    @Insert("<script>" +
            "insert into batch_test (username,password,address) values" +
            "<foreach collection='list' item='entity' separator=','>" +
            "(#{entity.username},#{entity.password},#{entity.address})" +
            "</foreach>" +
            "</script>")
    int batchInsert(@Param("list") List<BatchTestDO> list);
    @Test
    public  void test003(){
        List<BatchTestDO> data = generateData();
        long start=System.currentTimeMillis();
        batchTestMapper.batchInsert(data);
        System.out.println(String.format("用时:%d",System.currentTimeMillis()-start));
    }
image.png

用了5303ms

4.mybatis-plus的批量插入方式

    @Test
    public void test004(){
        List<BatchTestDO> data = generateData();
        long start=System.currentTimeMillis();
        batchTestMapper.insertBatchSomeColumn(data);
        System.out.println(String.format("用时:%d",System.currentTimeMillis()-start));
    }
image.png

用了5163ms

以上程序执行的时候,打开数据库的general_log日志配置,可以看到他们发送到mysql服务端的sql语句都是一样的,insert into table (a,b,c) values (x,x,x),(x,x,x)...这样的

image.png

测试用例2的速度比1 3 4都快的原因,个人认为应该是在于mybatis框架在读取对应字段值的时候用的java反射导致的耗时。

接下来说下rewriteBatchedStatements的作用,这个参数的意义是:在底层jdbc是通过测试用例2这样的批量处理的方式插入数据的时候,如果rewriteBatchedStatements设为true,jdbc驱动就会把sql在本地客户端拼接成insert into table (a,b,c) values (x,x,x),(x,x,x)...这样的sql发送到服务端(这里注意,数据量很多的时候,并不会把所有数据拼接成一条sql,而是分批次拼接,因为mysql每次传输的sql大小是有限制的),否则测试用例2这样的批量插入和一条一条数据的插入没有任何区别。

接下来就把rewriteBatchedStatements去掉再测一遍

image.png

290秒。。。。。


image.png

43秒


image.png
5535ms
image.png
5686ms

测试用例1,2用了很长时间,结合mysql日志可以看到都是一条条单独的inert语句,而3和4之所以不影响是因为他们拼接sql的操作是在mybatis里完成的而不是jdbc驱动,至于1和2在这种情况下为什么会差这么多,暂时还没找到原因

上一篇下一篇

猜你喜欢

热点阅读