最近公司项目使用jdbcTemplate的batchupdate方法进行批量数据插入,但是效率很慢,上源码

String sqlStr = "INSERT INTO " + tableName + "(xh,timetemp,row_num) VALUES(?,?,?)";
        List<Object[]> batchArgs = new ArrayList<>();
        log.info("开始循环赋值数据。。。");
        for (BiTempYw entity : items) {
            Object[] objects = new Object[3];
            objects[0] = entity.getXh();
            objects[1] = entity.getTimetemp();
            objects[2] = entity.getRownum();
            batchArgs.add(objects);
        }
        return new JdbcTemplate(dsm.getDataSource()).batchUpdate(sqlStr, batchArgs);
通过执行,发现在插入50000条数据时,耗时需要4分钟左右,因为项目中有许多的表需要执行,这样耗时太久,达不到要求,所以开始进行优化。

在网上查了许多文章,也看了很多的博客,其中大部分都是转载的,主要是在数据库连接的url中添加rewriteBatchedStatements=true的设置,但是这个只适用于mysql驱动,公司使用的是sqlserver,经过测试,无效。

也尝试过不使用jdbc封装,改用原生数据库连接并执行,但是依旧无效。

经过一系列尝试,最终发现,使用原生PreparedStatement的executeBatch()方法进行批量插入时,如果没有关闭自动提交,则在提交时,会不断的进行数据库commit操作,导致大量耗时。

经过修正后,代码如下:

String sqlStr = "INSERT INTO " + tableName + "(xh,row_num) VALUES(?,?)";
        /*List<Object[]> batchArgs = new ArrayList<>();
        for (BiTempOds entity : items) {
            Object[] objects = new Object[2];
            objects[0] = entity.getXh();
            objects[1] = entity.getRownum();
            batchArgs.add(objects);
        }
        return new JdbcTemplate(dsm.getDataSource()).batchUpdate(sqlStr, batchArgs);*/

        Connection conn = null;
        PreparedStatement ps = null;

        try {
            log.info("开始向临时表写入数据。。。");
            conn = dsm.getDataSource().getConnection();
            start=System.currentTimeMillis();
            ps = conn.prepareStatement(sqlStr);
            conn.setAutoCommit(false);

            for (int i = 0; i <items.size(); i++) {

                ps.setString(1, items.get(i).getXh());
                ps.setInt(2, Objects.isNull(items.get(i).getRownum()) ? 0 : items.get(i).getRownum());

                //将上述语句加入到批处理
                ps.addBatch();
                /*if (i % 1000 == 0) {
                    ps.executeBatch();
                    conn.commit();
                    ps.clearBatch();
                }*/
            }
            ps.executeBatch();
            conn.commit();
            end=System.currentTimeMillis();
            log.info("结束向临时表写入数据。。。,用时:" + (end-start));
        } catch (SQLException e) {
            log.error("向临时表写入数据错误", e.fillInStackTrace());
            try {
                conn.rollback();
            } catch (SQLException ex) {
                log.error("回滚conn连接错误", ex.fillInStackTrace());
            }
        }finally {
            try {
                ps.close();
            } catch (SQLException e) {
                log.error("关闭预编译语句发生异常: "+e.getMessage());
            }
            try {
                conn.close();
            } catch (SQLException e) {
                log.error("关闭数据库链接发生异常: "+e.getMessage());
            }
        }
其中最主要的就是关闭自动提交
conn.setAutoCommit(false);

当然,关闭了自动提交功能,后面执行插入操作后,要手动提交和关闭。

经过测试,原先4分钟才能执行完成的数据,只需要1199ms,速度大大提升

至此记录,作为日后参考。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐