による Hadoop - The Definitive Guide
FileInputFormatsが定義する論理レコードは、通常、HDFSブロックにうまく収まりません。たとえば、TextInputFormatの論理レコードは行であり、HDFSの境界を頻繁に超えます。これはプログラムの機能には関係ありません。たとえば、行の欠落や破損はありませんが、データローカルマップ(つまり、ホストと同じホストで実行されているマップ)入力データ)は、いくつかのリモート読み取りを実行します。これが引き起こすわずかなオーバーヘッドは、通常は重要ではありません。
レコード行が2つのブロック(b1とb2)に分割されているとします。最初のブロック(b1)を処理するマッパーは、最後の行にEOLセパレータがないことに気付き、次のデータブロック(b2)から残りの行をフェッチします。
2番目のブロック(b2)を処理するマッパーは、最初のレコードが不完全であり、ブロック(b2)の2番目のレコードから処理を開始する必要があるとどのように判断しますか?
興味深い質問ですが、詳細についてはコードを見て時間を費やしました。これが私の考えです。分割は_InputFormat.getSplits
_によってクライアントによって処理されるため、FileInputFormatを見ると次の情報が得られます。
max(minSize, min(maxSize, blockSize))
として分割サイズを計算します。ここで、maxSize
は_mapred.max.split.size
_に対応し、minSize
は_mapred.min.split.size
_。上記で計算した分割サイズに基づいて、ファイルを異なるFileSplit
sに分割します。ここで重要なのは、各FileSplit
は、入力ファイルのオフセットに対応するstart
パラメーターで初期化されるです。その時点ではまだ行の処理はありません。コードの関連部分は次のようになります。
_while (((double) bytesRemaining)/splitSize > SPLIT_SLOP) {
int blkIndex = getBlockIndex(blkLocations, length-bytesRemaining);
splits.add(new FileSplit(path, length-bytesRemaining, splitSize,
blkLocations[blkIndex].getHosts()));
bytesRemaining -= splitSize;
}
_
その後、LineRecordReader
で定義されているTextInputFormat
を見ると、そこで行が処理されます:
LineRecordReader
を初期化すると、LineReader
上の行を読み取ることができる抽象化であるFSDataInputStream
をインスタンス化しようとします。 2つのケースがあります。CompressionCodec
が定義されている場合、このコーデックは境界の処理を担当します。おそらくあなたの質問には関係ありません。ただし、コーデックがない場合は、興味深いことになります。start
のInputSplit
が0と異なる場合、1文字をバックトラックし、最初の行をスキップします\ nまたは\ r\n(Windows)で識別される遭遇!バックトラックは重要です。ライン境界が分割境界と同じ場合、有効なラインをスキップしないようにするためです。関連するコードは次のとおりです。
_if (codec != null) {
in = new LineReader(codec.createInputStream(fileIn), job);
end = Long.MAX_VALUE;
} else {
if (start != 0) {
skipFirstLine = true;
--start;
fileIn.seek(start);
}
in = new LineReader(fileIn, job);
}
if (skipFirstLine) { // skip first line and re-establish "start".
start += in.readLine(new Text(), 0,
(int)Math.min((long)Integer.MAX_VALUE, end - start));
}
this.pos = start;
_
したがって、分割はクライアントで計算されるため、マッパーを順番に実行する必要はありません。すべてのマッパーは、最初の行を破棄するかどうかをすでに知っています。
したがって、基本的に同じファイルに各100Mbの2行があり、単純化するために、分割サイズが64Mbであるとしましょう。次に、入力分割が計算されると、次のシナリオが発生します。
Map Reduceアルゴリズムは、ファイルの物理ブロックでは機能しません。論理的な入力分割で機能します。入力分割は、レコードが書き込まれた場所によって異なります。レコードは2つのマッパーにまたがることがあります。
[〜#〜] hdfs [〜#〜]が設定され、非常に大きなファイルを大きなブロック(たとえば、128MB)に分割し、これらのブロックのコピーを3つ保存します。クラスター内の異なるノード。
HDFSはこれらのファイルの内容を認識しません。レコードはBlock-aで開始された可能性がありますが、そのレコードの終わりはBlockに存在する可能性があります-b。
この問題を解決するために、Hadoopは、入力分割と呼ばれるファイルブロックに格納されたデータの論理表現を使用します。 MapReduceジョブクライアントがinput splits、を計算すると、ブロック内の最初のレコード全体がどこから始まり、ブロック内の最後のレコードがどこまで終わるかがわかります。
キーポイント:
ブロック内の最後のレコードが不完全な場合、入力分割には次のブロックの位置情報と、レコードを完了するために必要なデータのバイトオフセットが含まれます。
下の図をご覧ください。
これをご覧ください 記事 および関連するSEの質問: Hadoop/HDFSファイル分割について
詳細は documentation から読むことができます
Map-Reduceフレームワークは、ジョブのInputFormatに依存して次のことを行います。
InputSplit[] getSplits(JobConf job,int numSplits
)は、これらのことを処理するAPIです。FileInputFormat 、これはInputFormat
実装getSplits
()メソッドを拡張します。 grepcode でこのメソッドの内部を見てください
私はそれを次のように見ます:InputFormatは、データの性質を考慮してデータを論理的な分割に分割する責任があります。
ジョブに大きなレイテンシを追加する可能性はありますが、それを妨げるものはありません-必要な分割サイズ境界の周りのすべてのロジックと読み取りは、ジョブトラッカーで行われます。
最も単純なレコード対応入力フォーマットはTextInputFormatです。それは次のように機能しています(コードから理解している限り)-入力形式は、行に関係なくサイズで分割を作成しますが、LineRecordReaderは常に:
a)最初のスプリットではない場合、スプリット(またはその一部)の最初の行をスキップします
b)最後の分割の境界の後の1行を読み取ります(データが使用可能な場合、最後の分割ではありません)。
私が理解したことから、最初のブロックに対してFileSplit
が初期化されると、デフォルトのコンストラクターが呼び出されます。したがって、startとlengthの値は最初はゼロです。最初のブロックの処理が終了するまでに、最後の行が不完全な場合、長さの値は分割の長さより大きくなり、次のブロックの最初の行も読み取ります。このため、最初のブロックの開始値はゼロより大きくなり、この条件下では、LineRecordReader
は2番目のブロックの最初の行をスキップします。 ( source を参照)
最初のブロックの最後の行が完了した場合、長さの値は最初のブロックの長さと等しくなり、2番目のブロックの開始の値はゼロになります。その場合、LineRecordReader
は最初の行をスキップせず、最初から2番目のブロックを読み取ります。
理にかなっていますか?
LineRecordReader.Javaのhadoopソースコードからコンストラクタ:いくつかのコメントを見つけます:
// If this is not the first split, we always throw away first record
// because we always (except the last split) read one extra line in
// next() method.
if (start != 0) {
start += in.readLine(new Text(), 0, maxBytesToConsume(start));
}
this.pos = start;
このことから、hadoopは各分割ごとに1行余分に読み込み(現在の分割の最後に、次の分割の次の行を読み込む)、最初の分割でない場合は最初の行が破棄されると信じています。行レコードが失われたり不完全になることはありません
マッパーは通信する必要はありません。ファイルブロックはHDFSにあり、現在のマッパー(RecordReader)は、行の残りの部分を持つブロックを読み取ることができます。これはバックグラウンドで発生します。