09.並行処理(split、partition)概要
概要
Spring Batchにおける並行処理(split、partition)についてみていきます。
並行処理とは、スレッドを複数たてて処理を同時に行うことを言います(定義が間違っていたらすみません。この記事ではこの定義で記述していきます)。
処理スピードを上げることが目的です。
しかし、よく考えなければ逆に遅くなることもあるので、注意が必要です。
また、並行処理とSpring Remoteを併用することで、並列処理も可能になります。
並列処理とは、複数のPCで分散処理をすること言います。これもこの記事での定義としておきます。
ここでは、SpringBatchが用意する2つの並行処理、split、partitionの概要のみを記述します。
実際の使用サンプルは、別の記事に記述しますのでそちらを参照してみてください。
splitによる並行処理の概要
説明の開始の前に、まずは、ステップの動作のイメージ図と、ジョブ設定の記述イメージを示します。
【Spring Batchのsplit設定イメージ】
<job id="splitJob" xmlns="http://www.springframework.org/schema/batch">
<step id="step1" next="split1" />
<split id="split1" task-executor="taskExecutor" next="step4" >
<flow>
<step id="step2" />
</flow>
<flow>
<step id="step3" />
</flow>
</split>
<step id="step4" />
</job>
splitもpartitionも、並行処理であることは同じです。
splitの特徴は、上記のように並行処理するステップを明示的に指定するところです。
上記の場合、指定したstep2とstep3は同時に実行されます。
そして両方とも完了した時点でstep4に遷移します。
またflowタグの中にはstepを複数記述することも可能です。
flowタグ内はシーケンシャル(順番)にステップが実行されます。
partitionによる並行処理の概要
こちらもまずはイメージを示します。
【Spring Batchのpartition設定イメージ】
<job id="partitionJob" xmlns="http://www.springframework.org/schema/batch">
<step id="step1" next="masterStep" />
<step id="masterStep" next="step3">
<partition step="step2" partitioner="partitioner">
<handler grid-size="2" task-executor="taskExecutor" />
</partition>
</step>
<step id="step3" />
</job>
<step id="step2" xmlns="http://www.springframework.org/schema/batch" >
<tasklet>
<chunk reader="itemReader" writer="itemWriter" commit-interval="5" />
</tasklet>
</step>
partitionの特徴は、並行処理が1つのステップになっているところです。
このステップをMaster Stepと呼びます。
Masterは、splitのときと同様にスレッドを作成します。
ただし、作成するスレッドの数と、それらのスレッドに割り当てるステップはMasterが自動で決めます。
この点もsplitと違う点です。
以下、具体例を揚げることにします。
【具体例】
ファイルを処理することとしますが、/temp/target*.csv のような指定の仕方で、実行時に処理ファイル数が決まるものとします。
このファイルを1スレッド1ファイルという形で並行処理することを考えます。
ただし、処理内容はどのファイルも同じとします。
itemReaderでファイルを読み込んで、itemWriterでDBに書き込むものとします。
このチャンク処理をstep2として定義しておきます。
ここでは実行時に2ファイルが存在したとします。
動作:
partitionを使用した方法では、以下のように動作します。
①スレッド1を作成します。
②step2を複製し、スレッド1に割り当てて、ファイル1に対して処理を実行します(非同期処理)。
③スレッド2を作成します。
④step2を複製し、スレッド2に割り当てて、ファイル2に対して処理を実行します(非同期処理)。
⑤②、④の処理が両方とも終了した段階で、partitionのステップを終了します。
※より詳細な説明はサンプルを参照してください。
対象ファイルは /temp/target*.csv のように複数指定することができるため、実際にはファイルの数だけスレッドを自動で
作成することになります。
もちろんスレッドの最大数を設定するので、無限の数のスレッドを作成しようとはしません。
最大数を超えた数のファイルが存在するときは、どれかのスレッドが終わったときに次のファイルが割り当てられます。
【上記説明の注意点(stepの複製について)】
上記では説明を簡易にするため、少し不正確な記述をしています。
それは、「stepの複製を作る」という部分です。
実際にはstepのインスタンスはスレッドすべてで同じになります。
ただし、step内に設定するTasklet、ItemReader、ItemProcessor、ItemWriterなどのscopeを"step"にすることで
step実行毎にそれぞれのインスタンスを生成することができます。
つまり、scope="step"にすることで、複製したのとほぼ同じ効果があることが分かるかと思います。
通常partitionを使用するときはそうするため上記のような書き方をしました。
ただ、これだけですと他にもいろいろ疑問が湧いてきます。
(ファイル名をItemReaderなどに設定するのはどうやるのか?など)
このあたりはサンプルの記事で見ていこうと思いますので、そちらを参照ください。
splitとpartitionの違い
結局2つの主な違いは、スレッドの数を自分で明示に設定するか、それとも、一定のルールに従って自動で設定するか、ということになります。
(つまりスケーリングの違いが大きな違いなのだと思います。)
以下、主な違いを表にしてみました。
split、partition並行処理の処理シーケンスのイメージ
処理シーケンスを知ることで、並行処理の動作をより深く理解することができると思います。
以下に大まかな処理イメージを記述します。
各インターフェースは具象クラスを持っています。javadocを参照してみてください。
動作としては、ステップの具象クラスであるPartitionStepがPartitionHandlerを呼び出します。
結局はPartitionHandlerが並行処理のほとんどを行うことになります。
【Partitionerの役割】
Partitionerは、Step Execution Contextを処理を分割する個数だけ作成します。
このExecutionContextには、データをあらかじめ設定しておくことができます。
StepはExecutionContextに設定された値により処理を変えることができるため、間接的に、partitionerが
スレッド毎に処理内容を変更する役割を果たしています。
【StepExecutionSplitterの役割】
StepExecutionを作成します。
このとき、partitionerを使用してStepExecutionContextを取得し、作成したStepExecutionに設定します。
また、既に実行したStepExecutionをJobRepositoryから取得し、リスタートするための準備をする役割も担当しています。
【PartitionHandlerの役割】
StepExecutionSplitterを使用してStepExectionを取得し、実際に指定のステップを実行します。
このとき、スレッドを作成してStepを実行していきます。
【partitionを使用する場合の補足情報】
partitionを使用する方法では、自動でStepが複製されます。
このとき、Step名も重ならないように自動で名前が付けられます。
Step名はStepExecutionSplitterが自動で付与します。
命名の例としては、"step2:partition0"、"step2:partition1"のようになります。
Created Date: 2011/02/03