Hadoopはどのようにしてレコードをブロック境界にまたがって処理しますか?


119

による Hadoop - The Definitive Guide

FileInputFormatsが定義する論理レコードは、通常、HDFSブロックにきちんと適合しません。たとえば、TextInputFormatの論理レコードは線であり、HDFSの境界を越える頻度が高くなります。これは、プログラムの機能には影響しません。たとえば、行が失われたり壊れたりすることはありません。ただし、データローカルマップ(つまり、ローカルの同じホストで実行されているマップ)入力データ)は、いくつかのリモート読み取りを実行します。これが引き起こすわずかなオーバーヘッドは、通常は重要ではありません。

レコード行が2つのブロック(b1とb2)に分割されているとします。最初のブロック(b1)を処理するマッパーは、最後の行にEOLセパレータがないことに気づき、次のデータブロック(b2)から行の残りをフェッチします。

2番目のブロック(b2)を処理するマッパーは、最初のレコードが不完全であり、ブロック(b2)の2番目のレコードから処理を開始する必要があるとどのように判断しますか?

回答:


160

興味深い質問ですが、コードの詳細を確認するのに少し時間を費やしました。ここに私の考えを示します。分割はクライアントによってによって処理されるInputFormat.getSplitsため、FileInputFormatを見ると次の情報が得られます。

  • 各入力ファイルのために、ファイルの長さを取得し、ブロックサイズとして分割サイズを計算するmax(minSize, min(maxSize, blockSize))場合にmaxSize相当するが、へmapred.max.split.sizeminSizeなりますmapred.min.split.size
  • FileSplit上記で計算された分割サイズに基づいて、ファイルを異なるに分割します。ここで重要なのは、それぞれFileSplitstart入力ファイルのオフセットに対応するパラメーターで初期化されることです。その時点ではまだ回線の処理はありません。コードの関連部分は次のようになります。

    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ここで行が処理されます。

  • あなたがあなたのを初期化するときLineRecordReaderLineReaderそれは行を読み取ることができるようにする抽象であるaをインスタンス化しようとしますFSDataInputStream。2つのケースがあります。
  • ある場合にCompressionCodec定義されたが、その後、このコーデックは、境界を処理する責任があります。おそらくあなたの質問には関係ありません。
  • ただし、コーデックがない場合、それが興味深いところです。もしが0と異なる場合はstart1文字バックトラックし、\ nまたは\ r \ n(Windows)で識別される最初の行をスキップします。行の境界が分割された境界と同じである場合、有効な行をスキップしないようにするため、バックトラックは重要です。関連するコードは次のとおりです。InputSplit

    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であるとしましょう。次に、入力分割が計算されると、次のシナリオになります。

  • このブロックへのパスとホストを含​​む分割1。開始時に初期化200-200 = 0Mb、長さ64Mb。
  • 分割2は、開始時に初期化200-200 + 64 = 64Mb、長さ64Mb。
  • 開始時に分割3が初期化された200-200 + 128 = 128Mb、長さ64Mb。
  • 分割4は、開始時に初期化200-200 + 192 = 192Mb、長さ8Mbです。
  • マッパーAはスプリット1を処理し、開始は0であるため、最初の行をスキップせず、64Mbの制限を超える完全な行を読み取るため、リモート読み取りが必要です。
  • マッパーBはスプリット2を処理します。開始は!= 0であるため、64Mb-1バイトの後の最初の行をスキップします。これは、まだスプリット2にある100Mbの行1の終わりに対応するため、スプリット2に28Mbの行があります。リモートは残りの72Mbを読み取ります。
  • Mapper Cはスプリット3を処理します。開始は!= 0なので、128Mb-1バイトの後の最初の行をスキップします。これは、ファイルの終わりである200Mbの行2の終わりに対応するため、何もしません。
  • マッパーDは、192Mb-1バイトの後に改行を探すことを除いて、マッパーCと同じです。

また、@ PraveenSripatiは、境界が\ r \ nリターンで\ rにあるエッジケースがLineReader.readLine関数で処理されることを言及する価値があります。それはあなたの質問には関係ないと思いますが、必要に応じて詳細を追加できます。
Charles Menguy 2013年

入力に正確に64MBの2つのラインがあり、InputSplitsがラインの境界で正確に発生するとします。!だから、マッパーは常に開始理由= 0の第二のブロックで行を無視します
PraveenさんSripati

6
@PraveenSripatiその場合、2番目のマッパーにはstart!= 0が表示されるため、最初の行の\ nの直前に戻り、次の\ nまでスキップします。したがって、1行目はスキップされますが、2行目は期待どおりに処理されます。
Charles Menguy、2013年

@CharlesMenguyファイルの最初の行がどういうわけかスキップされる可能性はありますか?具体的には、最初の行にkey = 1と値aがあり、ファイル内のどこかに同じキーを持つ2つの行、key = 1、val = bとkey = 1、val = cがあります。問題は、私のレデューサーは{1、[a、b、c]}ではなく{1、[b、c]}と{1、[a]}を取得することです。ファイルの先頭に新しい行を追加しても、これは起こりません。理由は何ですか、サー?
神戸湾ケノービ

@CharlesMenguy HDFS上のファイルがバイナリファイルの場合(\r\n, \nレコードの切り捨てを表すテキストファイルとは対照的ですか)。
CᴴᴀZ

17

Map Reduceアルゴリズムは、ファイルの物理ブロックでは機能しません。論理的な入力分割で機能します。入力分割は、レコードが書き込まれた場所によって異なります。レコードは2つのマッパーにまたがることがあります。

HDFSのセットアップ方法は、非常に大きなファイルを大きなブロック(128MBなど)に分割し、これらのブロックの3つのコピーをクラスター内の異なるノードに格納します。

HDFSはこれらのファイルの内容を認識しません。レコードはブロックaで開始された可能性がありますが、そのレコードの終わりはブロックbに存在する可能性があります。

この問題を解決するために、Hadoopは、入力分割と呼ばれる、ファイルブロックに格納されたデータの論理表現を使用します。MapReduceのジョブクライアントが計算した場合、入力分割をそれがブロックの最初のレコード全体が始まり、どこどこ割り出しブロック両端の最後のレコード

重要なポイント:

ブロックの最後のレコードが不完全な場合、入力分割には、次のブロックの位置情報と、レコードを完了するために必要なデータのバイトオフセットが含まれます。

下の図を見てください。

ここに画像の説明を入力してください

この記事と関連するSEの質問をご覧くださいHadoop / HDFSファイル分割について

詳細はドキュメントから読むことができます

Map-Reduceフレームワークは、ジョブのInputFormatに依存して次のことを行います。

  1. ジョブの入力仕様を検証します。
  2. 入力ファイルを論理的なInputSplitに分割し、それぞれを個別のMapperに割り当てます。
  3. 次に、各InputSplitは、処理のために個々のマッパーに割り当てられます。スプリットはタプルかもしれませんInputSplit[] getSplits(JobConf job,int numSplits)は、これらのことを処理するAPIです。

FileInputFormatはInputFormat実装されたgetSplits()メソッドを拡張します。このメソッドの内部をgrepcode見てください。


7

私はそれを次のように見ています:InputFormatは、データの性質を考慮して、データを論理的な分割に分割する責任があります。
ジョブに大幅なレイテンシを追加する可能性はありますが、それを妨げるものは何もありません。すべてのロジックと必要な分割サイズの境界周辺の読み取りは、ジョブトラッカーで行われます。
最も単純なレコード対応の入力形式はTextInputFormatです。それは次のように機能しています(コードから理解できる限り)-入力形式は、行に関係なくサイズによって分割を作成しますが、LineRecordReaderは常に:
a)分割されていない場合(またはその一部)の最初の行をスキップします最初のスプリット
b)最後にスプリットの境界の後ろの1行を読み取ります(データがある場合、最後のスプリットではありません)。


Skip first line in the split (or part of it), if it is not the first split-非最初のブロックの最初のレコードが完了している場合、このロジックがどのように機能するかは不明です。
Praveen Sripati 2013年

私がコードを見る限り-各スプリットはそれが持っているものを読みます+次の行。したがって、改行がブロック境界上にない場合は問題ありません。改行が正確にブロック境界にある場合の正確な処理方法-理解する必要があります-私はもう少しコードを読みます
David Gruzman

3

私が理解したことからFileSplit、が最初のブロックに対して初期化されると、デフォルトのコンストラクターが呼び出されます。したがって、開始と長さの値は最初はゼロです。最初のブロックの処理が終了するまでに、最後の行が不完全な場合、長さの値は分割の長さよりも大きくなり、次のブロックの最初の行も読み取られます。このため、最初のブロックの開始の値はゼロより大きくなり、この状態でLineRecordReaderは、2番目のブロックの最初の行がスキップされます。(出典を参照)

最初のブロックの最後の行が完成した場合、長さの値は最初のブロックの長さに等しくなり、2番目のブロックの開始の値はゼロになります。その場合、LineRecordReaderは最初の行をスキップせず、最初から2番目のブロックを読み取ります。

理にかなっていますか?


2
このシナリオでは、特定のブロックの最後の行が完了していない場合、マッパーは互いに通信し、ブロックを順番に処理する必要があります。これが機能するかどうかはわかりません。
Praveen Sripati 2013年

1

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行余分に(現在の分割の終わりに、次の分割で次の行を読み取る)読み取り、最初の分割でない場合、最初の行は破棄されます。行レコードが失われたり不完全になったりしないように


0

マッパーは通信する必要はありません。ファイルブロックはHDFSにあり、現在のマッパー(RecordReader)は、行の残りの部分を持つブロックを読み取ることができます。これは裏で起こります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.