(线程学习)ibatis中的批量更新过慢

发布时间:2024年01月11日

进入batchUpdate源码

    protected ThreadLocal localSqlMapSession = new ThreadLocal();   

    protected SqlMapSessionImpl getLocalSqlMapSession() {
        SqlMapSessionImpl sqlMapSession =                 
           (SqlMapSessionImpl)this.localSqlMapSession.get();
        if (sqlMapSession == null || sqlMapSession.isClosed()) {
            sqlMapSession = new SqlMapSessionImpl(this);
            this.localSqlMapSession.set(sqlMapSession);
        }

        return sqlMapSession;
    }  


  public int batchUpdate(String sqlId, List list) {
        try {
            if (list == null) {
                throw new NullPointerException();
            } else {
                this.getLocalSqlMapSession().startBatch();

                for(int i = 0; i < list.size(); ++i) {
                    this.getLocalSqlMapSession().update(sqlId, list.get(i));
                }

                return this.getLocalSqlMapSession().executeBatch();
            }
        } catch (SQLException var4) {
            var4.printStackTrace();
            throw new RuntimeException(var4.getMessage(), var4);
        }
    }

我们关注

  • ? ?ThreadLocal
  • (1)this.getLocalSqlMapSession().startBatch();? -- 这里设置为true
    • 此处的作用是在后续源码中,他会判断?
      if (statementScope.getSession().isInBatch()) {????????
    • 如果为true ,就会把sql 放在一起List集合,再批量执行,否则直接执行!
  • (2)this.getLocalSqlMapSession().update(sqlId, list.get(i));? ?此处就会去做判断
  • (3)this.getLocalSqlMapSession().executeBatch();? ?开始批量更新

若想要优化批量更新操作,我们需要控制每次批量操作的数量,例如我遇到的生产环境需要导入1.5w条数据,直接for循环后,全部sql放在List集合再去执行,就会很慢,此时我们需要重写batchUpdate方法,控制每次批量执行的sql语句的量(这里我控制每次批量执行3000条sql语句)

 public int batchUpdate(List<ReceivableDataDetailEntity> list) {
        this.logger.info("=============进入测试的batchUpdate方法===========");
        try {
            if (list == null) {
                throw new NullPointerException();
            } else {
                this.sqlMapClient.startBatch();
                int batch = 0;
                for(int i = 0; i < list.size(); ++i) {
                    logger.info("=============第" +batch+ "次===========");
                    batch++;
                    this.sqlMapClient.update("receivableDataDetail.update", list.get(i));
                    if (batch%3000 == 0){
                        this.sqlMapClient.executeBatch();
                        this.sqlMapClient.startBatch();
                    }
                }
                this.logger.info("=============通用batchUpdate方法执行结束===========");
                return  this.sqlMapClient.executeBatch();
            }
        } catch (SQLException var4) {
            var4.printStackTrace();
            throw new RuntimeException(var4.getMessage(), var4);
        }
    }

做了如上操作后,生产导入的数据时间从4min提升到了30s

课外知识------------------------------------------------

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。每个线程使用的都是自己从内存中拷贝过来的变量的副本, 这样就不存在线程安全问题,也不会影响程序的执行性能。

ThreadLocal的接口方法:ThreadLocal类接口很简单,只有4个方法,ThreadLocal 可以存储任何类型的变量对象, get返回的是一个Object对象,但是我们可以通过泛型来制定存储对象的类型。

public T get() { } // 用来获取ThreadLocal在当前线程中保存的变量副本
public void set(T value) { } //set()用来设置当前线程中变量的副本
public void remove() { } //remove()用来移除当前线程中变量的副本
protected T initialValue() { } //initialValue()是一个protected方法,一般是用来在使用时进行重写的

有兴趣的可以看这个博主写的批量更新的源码,跟着他的思路慢慢看就能理顺

ibatis 批量入库优化_getlocalsqlmapsession-CSDN博客

文章来源:https://blog.csdn.net/weixin_47188125/article/details/135529174
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。