1

我用过 MyBatis-spring + Java。我需要在一个事务中将 >10000 条记录插入表中。为此,我使用了映射器:

<insert id="saveBulk" parameterType="List">
    INSERT INTO "quote" ("id", "mi_id", "timestamp", "open", "close", "low", "high", "volume", "period")
    VALUES
    <foreach collection="list" item="item" separator=",">
        ( #{item.key}, #{item.marketInstrumentKey}, #{item.timestamp}, #{item.open}, #{item.close}, #{item.low},
        #{item.high}, #{item.volume}, #{item.period}::quote_period)
    </foreach>
</insert>

并将 List 传递给该语句。对于 2000-3000 条记录,它的工作速度非常慢,但是插入 10000 条记录超过 4 分钟(我应该增加超时间隔)!通过 PgAdmin 将相同的 10000 条记录插入到同一个数据库的时间不到 10 秒。我试图追踪这个操作的处理,发现了一个瓶颈

public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

StatementHandler 的计算时间为几分钟,prepareStatement 的计算时间为几分钟。我明白,为什么会这样:10000 条记录,每条记录有 9 个字段。所有这些 100k 字段都应作为参数插入到语句中。我怎样才能加速这个过程?

更新:

我使用 sqlFactory 和 @Transactional 的“BATCH”模式实现了批量保存。这是 mybatis-spring XML 配置的配置:

    <bean id="sqlBatchTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory"/>
        <constructor-arg index="1" value="BATCH"/>
    </bean>

    <bean id="quoteBatchMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="tafm.dataaccess.mybatis.mapper.QuoteMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
        <property name="sqlSessionTemplate" ref="sqlBatchTemplate"/>
    </bean>
   <bean id="dataAccessBatch" class="tafm.dataaccess.DataAccess">
        <property name="quoteMapper" ref="quoteBatchMapper"/>
    </bean>

然后,我实现了一个“批处理”方法:

   @Transactional
    public void saveBulk(List<Quote> quotes) {


        for(Quote q:quotes) {
            mapper.save(q);
        }
    }

映射器- 是实体引用的 XML 映射器:

<insert id="saveBulk" parameterType="List">
    INSERT INTO "quote" ("id", "mi_id", "timestamp", "open", "close", "low", "high", "volume", "period")
    VALUES
    <foreach collection="list" item="item" index="index" separator=",">
        ( #{item.key}, #{item.marketInstrumentKey}, #{item.timestamp}, #{item.open}, #{item.close}, #{item.low},
        #{item.high}, #{item.volume}, #{item.period}::quote_period)
    </foreach>
</insert>

它工作得很快

4

1 回答 1

1

你可以做这样的事情。
映射器.xml

<insert id="saveBulk" parameterType="List">
    INSERT INTO "quote" ("id", "mi_id", "timestamp", "open", "close", "low", "high", "volume", "period")
    VALUES
    <foreach collection="list" item="item" separator=",">
        ( #{item.key}, #{item.marketInstrumentKey}, #{item.timestamp}, #{item.open}, #{item.close}, #{item.low},
        #{item.high}, #{item.volume}, #{item.period}::quote_period)
    </foreach>
</insert>

在 Java 文件中

public void insertBulkData(List<Item> list)
    {
        int total = list.size();        
        int interval = 1000;
        int from = 0;
        int to = 0;
        while (to <= total)
        {
            from = to == 0 ? 0 : to;
            to = (to + interval) <= total ? (to + interval) : total;
            saveBulk(list.subList(from, to));
            if (to == total)
                break;
        }
    }

上面的代码使用间隔为 1000.so 1000 条记录将一次插入。您也可以修改此间隔。
你必须调用insertBulkData(list)
我希望这对你有帮助。

于 2016-04-05T12:00:04.800 回答