はじめに
ETL処理で数百万件・数十GB規模のデータをファイルとして出力する場合、1つの巨大なファイルにまとめてしまうと、後続システムでの取り込みに時間がかかったり、メモリ不足やタイムアウトの原因になったりすることがあります。Amazon S3やSFTPなどへの出力では、適切なサイズに分割された複数ファイルとして書き出すことが、運用上のベストプラクティスです。
Xplentyでは、システム変数を設定するだけで、出力ファイルの分割を制御できます。分割の方法は大きく2つあります。
-
ファイルの個数で分割する …
_DEFAULT_PARALLELISM -
ファイルのサイズで分割する …
_BYTES_PER_REDUCER
本記事では、それぞれの仕組みと設定手順、使い分けのポイントを解説します。
出力ファイル分割の仕組み
Xplentyのジョブは、内部的にHadoopのMapReduce処理として実行されます。File Storage Destinationコンポーネントからファイルを出力する際、最終フェーズを処理するタスクの数だけ出力ファイルが生成されるのが基本的な仕組みです。
つまり、Reducerフェーズでジョブが終了する場合、「Reducerの数 = 出力ファイルの数」となります。今回紹介する2つのシステム変数は、いずれもこのReducerの数を制御することでファイル分割を実現します。
-
_DEFAULT_PARALLELISM… Reducerの数を直接指定する → ファイル個数を指定 -
_BYTES_PER_REDUCER… Reducer 1つあたりが処理するデータ量(バイト)を指定し、Reducer数を自動計算させる → ファイルサイズの目安を指定
公式ドキュメントにも、_DEFAULT_PARALLELISM のデフォルト値は0であり、その場合のReducer数は _BYTES_PER_REDUCER に基づいて計算される、と記載されています。
重要な前提:Reducerフェーズを経由させること
これらの変数はReducerフェーズに対して作用するため、データフローにReducerを発生させる処理が含まれていることが前提になります。Mapperだけで完結するシンプルなフロー(例:Source → Select → Destination)では、Reducerが起動せず、これらの変数を設定しても出力ファイルの分割数には反映されません。
確実にReducerフェーズを経由させる最も簡単な方法は、Destinationの直前にSortコンポーネントを配置することです。Sort・Aggregate・Joinなどの処理はReducerで実行されるため、これらのいずれかがフローに含まれていれば、変数の設定が有効に働きます。
方法1:ファイルの個数で分割する
概要
_DEFAULT_PARALLELISM は、パッケージ内で使用する並列Reduceタスクの数を指定するシステム変数です。ここに設定した数がReducerの数となり、そのまま出力ファイルの個数になります。
| 項目 | 内容 |
|---|---|
| 変数名 | _DEFAULT_PARALLELISM |
| 設定値 | 出力したいファイルの個数(整数) |
| デフォルト値 | 0(_BYTES_PER_REDUCER による自動計算) |
設定手順
- パッケージデザイナーを開き、パッケージの変数設定(System Variables)画面を開く
- System変数の
_DEFAULT_PARALLELISMに出力したいファイル数(例:10)を設定する
- データフローのDestination直前にSortコンポーネントが配置されていることを確認する
- パッケージを保存し、ジョブを実行する
- ジョブ実行後、出力先のディレクトリには指定した個数のファイルが生成される
ユースケース
- 後続の処理系が「N個のファイルを並列に読み込む」前提で設計されている場合
- Amazon Redshiftなどへ並列ロードするため、クラスタのスライス数に合わせてファイル数を揃えたい場合
- ファイル数を一定に保ち、運用・監視をシンプルにしたい場合
注意点
公式ドキュメントにも記載がある通り、Reducer数の効果はデータの量と分布に依存します。データに偏り(スキュー)がある場合、たとえば特定のキーにレコードが集中していると、Reducer数を増やしても処理が分散されず、ファイルサイズにも偏りが生じることがあります。
方法2:ファイルのサイズで分割する
概要
_BYTES_PER_REDUCER は、Reducer 1つあたりが処理するデータ量をバイト単位で指定するシステム変数です。_DEFAULT_PARALLELISM が0(デフォルト)の場合、Xplentyは「処理対象の総データ量 ÷ _BYTES_PER_REDUCER」からReducer数を自動計算します。結果として、各出力ファイルが指定サイズ前後に収まるように分割されます。
| 項目 | 内容 |
|---|---|
| 変数名 | _BYTES_PER_REDUCER |
| 設定値 | 1ファイルあたりの目安サイズ(バイト単位の整数) |
| 有効条件 |
_DEFAULT_PARALLELISM が0(未設定またはデフォルト)であること |
_BYTES_PER_REDUCERの設定例
| 目安サイズ | 設定値(バイト) |
|---|---|
| 128 MB | 134217728 |
| 256 MB | 268435456 |
| 512 MB | 536870912 |
| 1 GB | 1073741824 |
たとえば総量約2.5GBのデータに対して256MBを指定した場合、Reducerは約10個起動し、出力ファイルもおよそ10個に分割されます。
設定手順
- パッケージの**変数設定(Variables)**画面を開く
- System変数の
_BYTES_PER_REDUCERに1ファイルあたりの目安サイズをバイトで設定する(例:512MBなら536870912)
-
_DEFAULT_PARALLELISMを設定している場合は削除するか0にする(個数指定が優先されるため) - Destination直前にSortコンポーネントを配置し、ジョブを実行する
- ジョブ実行後、出力先のディレクトリには指定した名安のサイズでファイルが生成されることを確認する
ユースケース
- 後続システムに「1ファイルあたりの推奨サイズ/上限サイズ」がある場合(例:データウェアハウスへのロード最適化)
- 日によってデータ量が変動するジョブで、ファイル数ではなくファイルサイズを一定に保ちたい場合
- ファイルサイズの上限超過によるアップロード・転送エラーを避けたい場合
注意点
- 指定サイズはあくまで目安です。データの分布や圧縮設定(Gzip/Bzip2)によって、実際の出力ファイルサイズは前後します。特に圧縮を有効にした場合、出力ファイルは指定値より大幅に小さくなります(指定サイズは圧縮前のデータ量に基づくため)。
- レコード単位で分割されるため、1レコードが途中で分断されることはありません。
2つの方法の使い分け
| 観点 | _DEFAULT_PARALLELISM(個数指定) | _BYTES_PER_REDUCER(サイズ指定) |
|---|---|---|
| 制御対象 | 出力ファイルの個数 | 1ファイルあたりのデータ量の目安 |
| データ量が変動した場合 | ファイル数は固定、1ファイルのサイズが変動 | ファイルサイズは一定の目安、ファイル数が変動 |
| 優先順位 | 0より大きい値を設定するとこちらが優先 |
_DEFAULT_PARALLELISM が0のときに有効 |
| 向いているケース | 並列ロード先のスロット数に合わせたい | ファイルサイズの上限・推奨値に合わせたい |
判断の目安:
- 「ファイルは必ずN個にしたい」→
_DEFAULT_PARALLELISM - 「1ファイルをXXX MB程度に抑えたい」→
_BYTES_PER_REDUCER - 両方を設定した場合、
_DEFAULT_PARALLELISM(0より大きい値)が優先され、_BYTES_PER_REDUCERによる自動計算は行われません。
ベストプラクティス
-
Sortコンポーネントを忘れずに配置する
分割が効かない場合、まずReducerフェーズが発生しているかを疑ってください。Destination直前のSortが最も確実です。ソートキーは出力順を兼ねて業務上意味のあるカラム(タイムスタンプやIDなど)を指定すると一石二鳥です。 -
出力ファイル名のプレフィックスを活用する
File Storage Destinationの「File name prefix」で、デフォルトのpart-[mr]-から任意のプレフィックスに変更できます。変数を組み合わせてタイムスタンプ付きのファイル名にすると、分割ファイルの管理がしやすくなります。 -
小さすぎる分割は避ける
ファイルを細かく分割しすぎると、出力先(特にS3などのオブジェクトストレージ)で小さいファイルが大量に生成され、後続の読み取り効率がかえって悪化します。後続システムの推奨サイズに合わせて設定してください。 -
ジョブ実行後に実際の出力を確認する
初回設定時は、ジョブ完了後に出力先ディレクトリのファイル数とサイズを確認し、想定通りに分割されているか検証することをおすすめします。
まとめ
Xplentyでは、システム変数を2つ使い分けるだけで、出力ファイルの分割を柔軟に制御できます。
-
個数で分割 →
_DEFAULT_PARALLELISMにファイル数を設定 -
サイズで分割 →
_BYTES_PER_REDUCERにバイト数を設定(_DEFAULT_PARALLELISMは0のまま)
いずれの場合も、Sortコンポーネントなどを使ってReducerフェーズを経由させることが前提条件です。後続システムの要件(ファイル数固定か、サイズ上限か)に応じて適切な方法を選択してください。
