一尘不染

用输入参数列表更新大量行的最快方法 在MyBatis中到Oracle数据库

spring-boot

我通过将MyBatis中的变量List传递给Oracle DB来更新大量数据。

还不够高效,逐行提交更新sql查询,在sql查询或Executor.batch服务中进行循环的方式与我期望的相比太慢了。

//one of the method i use
<update id="updateAll">
    BEGIN
        <foreach collection="list" item="item" index="index" separator=";">
            UPDATE <include refid="tableName"/>
            <set>
                item_price = ${item.price}, update_time = ${item.updateTime}
            </set>
            WHERE id = ${item.id}
        </foreach>
    ;END;
</update>

通过尝试的方式,我的系统花费了10到30秒甚至更长的时间来完成更新。服务器每秒将有大约10,000行数据。有什么方法可以在1或2秒内在Oracledb中更新至少1-2k数据行?


阅读 388

收藏
2020-05-30

共1个答案

一尘不染

建议使用批处理执行器,但是您需要正确执行。
我注意到了两个问题。

  1. 设置适当的批次大小很重要。该时效率不高非常多端发送的所有数据。
  2. 使用${}引用参数会使每个语句唯一,并防止驱动程序重新使用该语句(基本上失去了批处理执行器的好处)。有关和之间的区别,请参见此常见问题解答#{}``${}

这是使用MyBatis的典型批处理操作。
由于最好batchSize取决于各种因素,因此应使用实际数据来衡量性能。

int batchSize = 1000;
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
  YourMapper mapper = sqlSession.getMapper(YourMapper.class);
  int size = list.size();
  for (int i = 0; i < size;) {
    mapper.update(list.get(i));
    i++;
    if (i % batchSize == 0 || i == size) {
      sqlSession.flushStatements();
      sqlSession.clearCache();
    }
  }
  sqlSession.commit();
}

这是update语句的有效版本。

<update id="update">
  UPDATE <include refid="tableName" />
  SET
    item_price = #{item.price},
    update_time = #{item.updateTime}
  WHERE id = #{item.id}
</update>
2020-05-30