web-dev-qa-db-ja.com

Spark=期間ごとにS3から複数のファイルを読み取る

説明

AWS Kinesis Firehoseにデータを送信するアプリケーションがあり、これによりデータがS3バケットに書き込まれます。 Firehoseは「yyyy/MM/dd/HH」形式を使用してファイルを書き込みます。

このサンプルS3パスのように:

s3://mybucket/2016/07/29/12

これで、特定の期間からデータを読み取る必要があるScalaで記述されたSparkアプリケーションがあります。開始日と終了日があります。データはJSON形式であるため、[ sqlContext.read.json()ではなくsc.textFile()

データをすばやく効率的に読み取るにはどうすればよいですか?

私は何を試しましたか?

  1. Wildcards-特定の日付のすべての時間または特定の月のすべての日付からデータを選択できます。次に例を示します。

    val df = sqlContext.read.json("s3://mybucket/2016/07/29/*")
    val df = sqlContext.read.json("s3://mybucket/2016/07/*/*")
    

    しかし、たとえば2016-07-29-2016-07-30のような数日の期間からデータを読み取る必要がある場合、同じ方法でワイルドカードアプローチを使用することはできません。

    それは私の次のポイントに私をもたらします...

  2. 複数のパスを使用するまたは this ソリューションのsamthebestによって提示されるディレクトリのCSVディレクトリをコンマで区切るのはsc.textFile()ではなくsqlContext.read.json()でのみ機能するようです。
  3. Union-cloudによる前のリンクの2番目の解決策は、各ディレクトリを個別に読み取り、それらを結合することを提案します。彼はRDD-sを結合することを提案していますが、DataFramesを結合するオプションもあります。特定の日付期間から日付文字列を手動で生成すると、存在しないパスを作成する場合がありますが、無視する代わりに読み取り全体が失敗します。代わりに、AWS SDKを使用し、AmazonS3Clientの関数listObjectsを使用して、iMKanchwalaのソリューションのようなすべてのキーを前のリンクから取得できます。

    唯一の問題は、データが常に変化していることです。 read.json()関数がすべてのデータを単一のパラメーターとして取得する場合、必要なすべてのデータを読み取り、データからjsonスキーマを推測できるほどスマートです。 2つのディレクトリを別々に読み取り、それらのスキーマが一致しない場合、これら2つのデータフレームの結合が問題になると思います。

  4. Glob(?)構文- これnhahtdhによる解決策は、オプション1および2は、日付とディレクトリをより詳細に、単一の「パス」として指定するオプションを提供するため、read.json()

    しかし、ここでも、欠落しているディレクトリに関してよく知られている問題が発生します。 20.07から30.07までのすべてのデータが必要だとしましょう。次のように宣言できます。

    val df = sqlContext.read.json("s3://mybucket/2016/07/[20-30]/*")
    

    しかし、たとえば7月25日のデータが欠落している場合、パス..16/07/25/は存在せず、関数全体が失敗します。

そして、要求された期間がたとえば25.11.2015-12.02.2016の場合、明らかに難しくなります。次に、プログラムで(Scalaスクリプトで)次のような文字列パスを作成する必要があります。

"s3://mybucket/{2015/11/[25-30],2015/12/*,2016/01/*,2016/02/[01-12]}/*"

そして、それを作成することにより、これらの25〜30および01〜12の間隔にすべて対応するパスがあることを何らかの方法で確認する必要があります。 (アスタリスクは存在するすべてを読み取るため、幸いなことに欠落しているディレクトリを処理します)

特定の日付間隔の間にディレクトリが見つからないために失敗する可能性なしに、単一のディレクトリパスから必要なデータをすべて一度に読み取るにはどうすればよいですか?

21
V. Samma

もっと簡単な解決策があります。 DataFrameReader API を見ると、.json(paths: String*)メソッドがあることがわかります。希望するパスのコレクションを構築し、必要に応じてグロブを使用せずに、メソッドを呼び出します(例:

val paths: Seq[String] = ...
val df = sqlContext.read.json(paths: _*)
12
Sim