为啥spring jdbcTemplate batchUpdate逐行插入

Posted

技术标签:

【中文标题】为啥spring jdbcTemplate batchUpdate逐行插入【英文标题】:why spring jdbcTemplate batchUpdate insert row by row为什么spring jdbcTemplate batchUpdate逐行插入 【发布时间】:2017-01-27 07:31:59 【问题描述】:

我有 200K 行要插入到一个数据库表中。我尝试在春季使用jdbcTemplate.batchUpdate,以便每批插入 10,000 个。但是,此过程消耗太多时间(200K 行需要 7 分钟)。所以在数据库方面,我检查select count(*) from table_X 插入的行数。我发现行数略有增加,而不是预期的 10K。任何人都可以解释是什么原因还是应该在数据库端配置的东西?

PS:我用的是sybase ....

【问题讨论】:

显示您的代码,您使用的是 BatchPreparedStatementSetter 吗?您是否在服务或 DAO 上使用 @Transactional 注释? @SheetalMohanSharma 我没有使用 spring,而是切换到本地 jdbc api 并自己处理事务。现在可以了。虽然还是很慢.... 实际上我删除了可能导致问题的@Transactional 注释......但我删除了它以加速插入。 ...没有帮助 检查 jdbc 连接设置,想不起来但有一些参数有助于加快处理速度... @SheetalMohanSharma 实际上,我的 jdbc 连接是通过 Spring 构建的 jdbcTemplate 获得的。我试图在配置文件中设置 EnableBulkLoad 参数...但它不起作用或此参数设置不正确。 :( 【参考方案1】:

网络上有很多可用的方法。 性能直接取决于

    您编写的代码 您正在使用的 JDBC 驱动程序 您正在使用的数据库服务器和连接数 表索引导致插入速度变慢

不看你的代码,任何人都可以猜到,但没有人能找到确切的解决方案。

方法一

//insert batch example
public void insertBatch(final List<Customer> customers)

  String sql = "INSERT INTO CUSTOMER " +
    "(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";

  getJdbcTemplate().batchUpdate(sql, new BatchPreparedStatementSetter() 

    @Override
    public void setValues(PreparedStatement ps, int i) throws SQLException 
        Customer customer = customers.get(i);
        ps.setLong(1, customer.getCustId());
        ps.setString(2, customer.getName());
        ps.setInt(3, customer.getAge() );
    

    @Override
    public int getBatchSize() 
        return customers.size();
    
  );

参考

https://www.mkyong.com/spring/spring-jdbctemplate-batchupdate-example/

http://docs.spring.io/spring-framework/docs/3.0.0.M4/reference/html/ch12s04.html

方法 2.1

//insert batch example
public void insertBatch(final List<Customer> customers)
    String sql = "INSERT INTO CUSTOMER " +
        "(CUST_ID, NAME, AGE) VALUES (?, ?, ?)";

    List<Object[]> parameters = new ArrayList<Object[]>();

    for (Customer cust : customers) 
        parameters.add(new Object[] cust.getCustId(),
            cust.getName(), cust.getAge()
        );
    
    getSimpleJdbcTemplate().batchUpdate(sql, parameters);

或者,您可以直接执行 SQL。

//insert batch example with SQL
public void insertBatchSQL(final String sql)

    getJdbcTemplate().batchUpdate(new String[]sql);


参考

https://www.mkyong.com/spring/spring-simplejdbctemplate-batchupdate-example/

方法 2.2

public class JdbcActorDao implements ActorDao 
    private SimpleJdbcTemplate simpleJdbcTemplate;

    public void setDataSource(DataSource dataSource) 
        this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
    

    public int[] batchUpdate(final List<Actor> actors) 
        SqlParameterSource[] batch = SqlParameterSourceUtils.createBatch(actors.toArray());
        int[] updateCounts = simpleJdbcTemplate.batchUpdate(
            "update t_actor set first_name = :firstName, last_name = :lastName where id = :id",
            batch);
        return updateCounts;
    

    //  ... additional methods

方法 2.3

public class JdbcActorDao implements ActorDao 
    private SimpleJdbcTemplate simpleJdbcTemplate;

    public void setDataSource(DataSource dataSource) 
        this.simpleJdbcTemplate = new SimpleJdbcTemplate(dataSource);
    

    public int[] batchUpdate(final List<Actor> actors) 
        List<Object[]> batch = new ArrayList<Object[]>();
        for (Actor actor : actors) 
            Object[] values = new Object[] 
                    actor.getFirstName(),
                    actor.getLastName(),
                    actor.getId();
            batch.add(values);
        
        int[] updateCounts = simpleJdbcTemplate.batchUpdate(
                "update t_actor set first_name = ?, last_name = ? where id = ?",
                batch);
        return updateCounts;
    

    //  ... additional methods

方法三:JDBC

dbConnection.setAutoCommit(false);//commit trasaction manually

String insertTableSQL = "INSERT INTO DBUSER"
            + "(USER_ID, USERNAME, CREATED_BY, CREATED_DATE) VALUES"
            + "(?,?,?,?)";
PreparedStatement = dbConnection.prepareStatement(insertTableSQL);

preparedStatement.setInt(1, 101);
preparedStatement.setString(2, "mkyong101");
preparedStatement.setString(3, "system");
preparedStatement.setTimestamp(4, getCurrentTimeStamp());
preparedStatement.addBatch();

preparedStatement.setInt(1, 102);
preparedStatement.setString(2, "mkyong102");
preparedStatement.setString(3, "system");
preparedStatement.setTimestamp(4, getCurrentTimeStamp());
preparedStatement.addBatch();
preparedStatement.executeBatch();

dbConnection.commit();

参考

https://www.mkyong.com/jdbc/jdbc-preparedstatement-example-batch-update/

/*Happy Coding*/

【讨论】:

感谢您的回复。我的代码是使用原生 JDBC 准备语句的方法 3。实际上。插入是由包含 10K 行的批处理有效地完成的。然而 10K 行需要 25 到 30 秒才能终止,这是不可接受的:( 我使用的是 Sybase 15.5,猜想这可能是 Sybase 的问题,因为我用相同的代码在 PostgreSQL 上进行了测试。对于 10K 行,PostgreSQL 需要不到 1 秒的时间。我曾向 DBA 团队寻求建议。感谢您提供上述示例。【参考方案2】:

尝试下面的连接字符串设置 - useServerPrepStmts=false&amp;rewriteBatchedStatements=true。没试过,但它来自我的书签。您可以在这些行上搜索..

Connection c = DriverManager.getConnection("jdbc:<db>://host:<port>/db?useServerPrepStmts=false&rewriteBatchedStatements=true", "username", "password");

【讨论】:

嗯,但仍然检查该帖子和该帖子中的链接 - 它可能会有所帮助 是的,谢谢你的建议,我会在找到解决方案时更新这篇文章【参考方案3】:

对我们来说,将代码移动到包装类并使用 @Transactional 注释批量插入方法确实解决了问题。

【讨论】:

以上是关于为啥spring jdbcTemplate batchUpdate逐行插入的主要内容,如果未能解决你的问题,请参考以下文章

(转)Spring JdbcTemplate 方法详解

Spring 从入门到精通系列 11—— Spring 中的 JdbcTemplate

Spring--JdbcTemplate

spring boot 与 JdbcTemplate 一起工作

Spring之004: jdbcTemplate基本使用Spring实物控制

spring5:JdbcTemplate