2

Spring Batch を使用して、MySQL データベース テーブルから 7,637,064 行を処理するプログラムを作成しています。小さなテーブルで成功しましたが、このテーブルの行数が多いため、JdbcCursorItemReader がカーソルを開こうとすると OutOfMemoryError 例外が発生します。

おそらく、より大きな Xmx を投げることでこれを解決できますが、Spring Batch にはこれを処理する方法が必要であり、構成の重要な部分が単に欠けている可能性があるようです。

春のバッチ構成:

  <job id="reportJob" xmlns="http://www.springframework.org/schema/batch">
    <step id="largeTableTransfer">
      <tasklet>
        <chunk reader="largeTableReader" processor="largeTableTransformer" writer="largeTableWriter"
          commit-interval="10" />
      </tasklet>
    </step>
  </job>

  <bean id="largeTableReader" class="org.springframework.batch.item.database.JdbcCursorItemReader">
    <property name="dataSource" ref="inputDataSource" />
    <property name="sql" value="select * from largeTable" />
    <property name="rowMapper">
      <bean class="myproject.reader.largeTableRowMapper" />
    </property>
  </bean>
  <bean id="largeTableTransformer" class="myproject.transformer.LargeTableTransformer" />
  <bean id="largeTableWriter" class="myproject.writer.JdbcLargeTableWriter">
    <property name="dataSource" ref="outputDataSource" />
  </bean>

JdbcCursorItemReaderで fetchSize を設定しても効果がないように見えます。最後まで実行できるようにする唯一の方法は、 maxRowsを小さい数に設定することですが、その数の行だけが処理されます。

関連するスタック トレース:

2012-11-21 11:25:29,931 DEBUG [org.springframework.batch.core.repository.dao.JdbcStepExecutionDao] - <Truncating long message before update of StepExecution, original message is: java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2734)
    at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
    at java.util.ArrayList.add(ArrayList.java:351)
    at com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:2821)
    at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:467)
    at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:2510)
    at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:1746)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2135)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2542)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1734)
    at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1885)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:93)
    at org.springframework.batch.item.database.JdbcCursorItemReader.openCursor(JdbcCursorItemReader.java:125)
    at org.springframework.batch.item.database.AbstractCursorItemReader.doOpen(AbstractCursorItemReader.java:401)
    at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.open(AbstractItemCountingItemStreamItemReader.java:134)
    at org.springframework.batch.item.support.CompositeItemStream.open(CompositeItemStream.java:93)
    at org.springframework.batch.core.step.tasklet.TaskletStep.open(TaskletStep.java:301)
    at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:192)
    at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:135)
    at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:61)
    at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:60)
    at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:144)
    at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:124)
    at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:135)
    at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:293)
    at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:120)
    at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:48)
    at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:114)
    at ifpress.ams2amx.ExampleJobConfigurationTests.testLaunchJob(ExampleJobConfigurationTests.java:32)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
4

1 に答える 1

3

MySql JDBC ドライバーに問題があり、ステートメント作成メソッドに渡したパラメーターに関係なく、データセット全体がメモリに読み込まれます。カーソルを正しく開く方法については、 http://dev.mysql.com/doc/refman/5.0/en/connector-j-reference-implementation-notes.htmlを参照してください。

これが私がそれを行う方法です:

<bean class="org.springframework.batch.item.database.JdbcCursorItemReader">
  <property name="verifyCursorPosition" value="false" />
   <property name="dataSource" ref="remoteDataSource" />
   <property name="rowMapper">
     <bean class="org.springframework.jdbc.core.SingleColumnRowMapper" />
   </property>
   <property name="fetchSize">
     <util:constant static-field="java.lang.Integer.MIN_VALUE" />
   </property>
   <property name="sql">
     <value>SELECT foo, bar FROM baz</value>
  </property>
</bean>
于 2012-11-22T00:50:13.123 に答える