批量操作符的MyBatis性能
MyBatis Performance of bulk operator
我用过MyBatis-spring + Java。我需要在一次交易中将 >10000 条记录插入 table。为此,我使用了映射器:
<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>
并向该语句传递一个列表。对于 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 config:
<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>
然后,我实现了一个"batch"方法:
@Transactional
public void saveBulk(List<Quote> quotes) {
for(Quote q:quotes) {
mapper.save(q);
}
}
mapper - 是 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>
它工作得很快
你可以这样做。
Mapper.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 条记录将插入 time.you 也可以修改此间隔。
您必须调用 insertBulkData(list)。
希望对您有所帮助。
我用过MyBatis-spring + Java。我需要在一次交易中将 >10000 条记录插入 table。为此,我使用了映射器:
<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>
并向该语句传递一个列表。对于 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 config:
<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>
然后,我实现了一个"batch"方法:
@Transactional
public void saveBulk(List<Quote> quotes) {
for(Quote q:quotes) {
mapper.save(q);
}
}
mapper - 是 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>
它工作得很快
你可以这样做。
Mapper.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 条记录将插入 time.you 也可以修改此间隔。
您必须调用 insertBulkData(list)。
希望对您有所帮助。