web-dev-qa-db-ja.com

Apache Sparkネストされた構造内の不要なParquet列を読み取るのはなぜですか?

私のチームは、Sparkを使用して生の区切りテキストファイルをParquetベースの「データレイク」にロードするETLプロセスを構築しています。 Parquet列ストアの約束の1つは、クエリが必要な「列ストライプ」のみを読み取ることです。

しかし、ネストされたスキーマ構造に対して予期しない列が読み取られているのがわかります。

実例を示すために、ScalaおよびSpark 2.0.1シェルを使用したPOC:

// Preliminary setup
sc.setLogLevel("INFO")
import org.Apache.spark.sql.types._
import org.Apache.spark.sql._

// Create a schema with nested complex structures
val schema = StructType(Seq(
    StructField("F1", IntegerType),
    StructField("F2", IntegerType),
    StructField("Orig", StructType(Seq(
        StructField("F1", StringType),
        StructField("F2", StringType))))))

// Create some sample data
val data = spark.createDataFrame(
    sc.parallelize(Seq(
        Row(1, 2, Row("1", "2")),
        Row(3, null, Row("3", "ABC")))),
    schema)

// Save it
data.write.mode(SaveMode.Overwrite).parquet("data.parquet")

次に、ファイルをDataFrameに読み戻し、列のサブセットに投影します。

// Read it back into another DataFrame
val df = spark.read.parquet("data.parquet")

// Select & show a subset of the columns
df.select($"F1", $"Orig.F1").show

これを実行すると、期待される出力が表示されます。

+---+-------+
| F1|Orig_F1|
+---+-------+
|  1|      1|
|  3|      3|
+---+-------+

しかし...クエリプランは少し異なるストーリーを示しています:

「最適化された計画」は次のことを示しています。

val projected = df.select($"F1", $"Orig.F1".as("Orig_F1"))
projected.queryExecution.optimizedPlan
// Project [F1#18, Orig#20.F1 AS Orig_F1#116]
// +- Relation[F1#18,F2#19,Orig#20] parquet

そして「説明」は示しています:

projected.explain
// == Physical Plan ==
// *Project [F1#18, Orig#20.F1 AS Orig_F1#116]
// +- *Scan parquet [F1#18,Orig#20] Format: ParquetFormat, InputPaths: hdfs://sandbox.hortonworks.com:8020/user/stephenp/data.parquet, PartitionFilters: [], PushedFilters: [], ReadSchema: struct<F1:int,Orig:struct<F1:string,F2:string>>

また、実行中に生成されたINFOログは、Orig.F2列が予期せず読み取られたことも確認します。

16/10/21 15:13:15 INFO parquet.ParquetReadSupport: Going to read the following fields from the Parquet file:

Parquet form:
message spark_schema {
  optional int32 F1;
  optional group Orig {
    optional binary F1 (UTF8);
    optional binary F2 (UTF8);
  }
}

Catalyst form:
StructType(StructField(F1,IntegerType,true), StructField(Orig,StructType(StructField(F1,StringType,true), StructField(F2,StringType,true)),true))

Dremel paper および Parquetドキュメント によると、複雑なネストされた構造の列は、個別に格納され、個別に取得可能である必要があります。

質問:

  1. この動作は現在のSparkクエリエンジンの制限ですか?言い換えると、Parquetはこのクエリの最適な実行をサポートしていますが、Sparkのクエリプランナーはナイーブですか?
  2. または、これは現在の寄木細工の実装の制限ですか?
  3. または、Spark APIを正しく使用していませんか?
  4. または、Dremel/Parquetカラムストレージがどのように機能するのか誤解していますか?

関連する可能性があります: クエリのパフォーマンスがSpark SQL? のネストされた列と異なるのはなぜですか?

22
Peter Stephens

これは、現時点ではSparkクエリエンジンの制限です。関連するJIRAチケットは以下のとおりです、sparkは、ネストされていない、Parquetの単純な型の述語プッシュダウンのみを処理しますStructTypes

https://issues.Apache.org/jira/browse/SPARK-17636

4
Ewan Leith