스파크> = 2.3.0
SPARK-22614 는 범위 분할을 노출합니다.
val partitionedByRange = df.repartitionByRange(42, $"k")
partitionedByRange.explain
// == Parsed Logical Plan ==
// 'RepartitionByExpression ['k ASC NULLS FIRST], 42
// +- AnalysisBarrier Project [_1#2 AS k#5, _2#3 AS v#6]
//
// == Analyzed Logical Plan ==
// k: string, v: int
// RepartitionByExpression [k#5 ASC NULLS FIRST], 42
// +- Project [_1#2 AS k#5, _2#3 AS v#6]
// +- LocalRelation [_1#2, _2#3]
//
// == Optimized Logical Plan ==
// RepartitionByExpression [k#5 ASC NULLS FIRST], 42
// +- LocalRelation [k#5, v#6]
//
// == Physical Plan ==
// Exchange rangepartitioning(k#5 ASC NULLS FIRST, 42)
// +- LocalTableScan [k#5, v#6]
SPARK-22389 는 데이터 소스 API v2 에서 외부 형식 파티셔닝을 노출합니다 .
스파크> = 1.6.0
Spark> = 1.6에서는 쿼리 및 캐싱을 위해 열별 분할을 사용할 수 있습니다. 참조 : SPARK-11410 및 SPARK-4849 사용 repartition
방법 :
val df = Seq(
("A", 1), ("B", 2), ("A", 3), ("C", 1)
).toDF("k", "v")
val partitioned = df.repartition($"k")
partitioned.explain
// scala> df.repartition($"k").explain(true)
// == Parsed Logical Plan ==
// 'RepartitionByExpression ['k], None
// +- Project [_1#5 AS k#7,_2#6 AS v#8]
// +- LogicalRDD [_1#5,_2#6], MapPartitionsRDD[3] at rddToDataFrameHolder at <console>:27
//
// == Analyzed Logical Plan ==
// k: string, v: int
// RepartitionByExpression [k#7], None
// +- Project [_1#5 AS k#7,_2#6 AS v#8]
// +- LogicalRDD [_1#5,_2#6], MapPartitionsRDD[3] at rddToDataFrameHolder at <console>:27
//
// == Optimized Logical Plan ==
// RepartitionByExpression [k#7], None
// +- Project [_1#5 AS k#7,_2#6 AS v#8]
// +- LogicalRDD [_1#5,_2#6], MapPartitionsRDD[3] at rddToDataFrameHolder at <console>:27
//
// == Physical Plan ==
// TungstenExchange hashpartitioning(k#7,200), None
// +- Project [_1#5 AS k#7,_2#6 AS v#8]
// +- Scan PhysicalRDD[_1#5,_2#6]
RDDs
Spark Dataset
( Dataset[Row]
aka 포함 DataFrame
) 와 달리 현재로서는 사용자 지정 파티 셔 너를 사용할 수 없습니다. 일반적으로 인위적인 분할 열을 생성하여이 문제를 해결할 수 있지만 동일한 유연성을 제공하지는 않습니다.
Spark <1.6.0 :
당신이 할 수있는 한 가지는 당신이 생성하기 전에 입력 데이터를 미리 분할하는 것입니다. DataFrame
import org.apache.spark.sql.types._
import org.apache.spark.sql.Row
import org.apache.spark.HashPartitioner
val schema = StructType(Seq(
StructField("x", StringType, false),
StructField("y", LongType, false),
StructField("z", DoubleType, false)
))
val rdd = sc.parallelize(Seq(
Row("foo", 1L, 0.5), Row("bar", 0L, 0.0), Row("??", -1L, 2.0),
Row("foo", -1L, 0.0), Row("??", 3L, 0.6), Row("bar", -3L, 0.99)
))
val partitioner = new HashPartitioner(5)
val partitioned = rdd.map(r => (r.getString(0), r))
.partitionBy(partitioner)
.values
val df = sqlContext.createDataFrame(partitioned, schema)
이후 DataFrame
에서 작성 RDD
파티션 레이아웃 기존 단지 간단한지도 상 필요 * 보존한다 :
assert(df.rdd.partitions == partitioned.partitions)
동일한 방법으로 기존의 파티션을 다시 분할 할 수 있습니다 DataFrame
.
sqlContext.createDataFrame(
df.rdd.map(r => (r.getInt(1), r)).partitionBy(partitioner).values,
df.schema
)
그래서 불가능하지 않은 것 같습니다. 그것이 의미가 있다면 문제는 남아 있습니다. 나는 대부분의 경우 다음과 같지 않다고 주장 할 것입니다.
재 파티션은 비용이 많이 드는 프로세스입니다. 일반적인 시나리오에서 대부분의 데이터는 직렬화, 셔플 및 역 직렬화되어야합니다. 반면에 사전 분할 된 데이터의 이점을 얻을 수있는 작업 수는 상대적으로 적으며 내부 API가이 속성을 활용하도록 설계되지 않은 경우 더 제한됩니다.
- 일부 시나리오에서는 조인하지만 내부 지원이 필요합니다.
- 창 함수는 일치하는 파티 셔너로 호출합니다. 위와 동일하며 단일 창 정의로 제한됩니다. 하지만 이미 내부적으로 분할되어 있으므로 사전 분할이 중복 될 수 있습니다.
- 간단한 집계
GROUP BY
-임시 버퍼 **의 메모리 사용량을 줄일 수 있지만 전체 비용은 훨씬 더 높습니다. groupByKey.mapValues(_.reduce)
(현재 동작) 대 reduceByKey
(사전 분할) 과 다소 동일합니다 . 실제로 유용 할 것 같지 않습니다.
- 로 데이터 압축
SqlContext.cacheTable
. 실행 길이 인코딩을 사용하는 것처럼 보이므로 적용 OrderedRDDFunctions.repartitionAndSortWithinPartitions
하면 압축률이 향상 될 수 있습니다.
성능은 키 배포에 크게 좌우됩니다. 치우친 경우 리소스 사용률이 최적화되지 않습니다. 최악의 시나리오에서는 작업을 전혀 완료 할 수 없습니다.
- 높은 수준의 선언적 API 사용의 요점은 낮은 수준의 구현 세부 정보에서 자신을 격리하는 것입니다. @dwysakowicz 및 @RomiKuntsman이 이미 언급했듯이 최적화는 Catalyst Optimizer 의 작업입니다. . 그것은 매우 정교한 짐승이며 내부에 훨씬 더 깊이 들어 가지 않고도 쉽게 향상시킬 수 있을지 의심 스럽습니다.
관련 개념
JDBC 소스로 파티셔닝 :
JDBC 데이터 소스는 predicates
인수를 지원 합니다. 다음과 같이 사용할 수 있습니다.
sqlContext.read.jdbc(url, table, Array("foo = 1", "foo = 3"), props)
술어 당 하나의 JDBC 파티션을 작성합니다. 개별 조건자를 사용하여 생성 된 집합이 분리되지 않은 경우 결과 테이블에 중복 항목이 표시됩니다.
partitionBy
방법 DataFrameWriter
:
Spark DataFrameWriter
는 partitionBy
쓰기시 데이터를 "분할"하는 데 사용할 수있는 방법을 제공합니다 . 제공된 열 세트를 사용하여 쓰기시 데이터를 분리합니다.
val df = Seq(
("foo", 1.0), ("bar", 2.0), ("foo", 1.5), ("bar", 2.6)
).toDF("k", "v")
df.write.partitionBy("k").json("/tmp/foo.json")
이를 통해 키 기반 쿼리에 대해 읽기시 술어 푸시 다운을 사용할 수 있습니다.
val df1 = sqlContext.read.schema(df.schema).json("/tmp/foo.json")
df1.where($"k" === "bar")
그러나 DataFrame.repartition
. 특히 다음과 같은 집계 :
val cnts = df1.groupBy($"k").sum()
여전히 다음이 필요합니다 TungstenExchange
.
cnts.explain
// == Physical Plan ==
// TungstenAggregate(key=[k#90], functions=[(sum(v#91),mode=Final,isDistinct=false)], output=[k#90,sum(v)#93])
// +- TungstenExchange hashpartitioning(k#90,200), None
// +- TungstenAggregate(key=[k#90], functions=[(sum(v#91),mode=Partial,isDistinct=false)], output=[k#90,sum#99])
// +- Scan JSONRelation[k#90,v#91] InputPaths: file:/tmp/foo.json
bucketBy
방법 DataFrameWriter
(스파크> = 2.0) :
bucketBy
와 유사한 응용 프로그램이 partitionBy
있지만 테이블 ( saveAsTable
) 에만 사용할 수 있습니다 . 버 케팅 정보를 사용하여 조인을 최적화 할 수 있습니다.
// Temporarily disable broadcast joins
spark.conf.set("spark.sql.autoBroadcastJoinThreshold", -1)
df.write.bucketBy(42, "k").saveAsTable("df1")
val df2 = Seq(("A", -1.0), ("B", 2.0)).toDF("k", "v2")
df2.write.bucketBy(42, "k").saveAsTable("df2")
// == Physical Plan ==
// *Project [k#41, v#42, v2#47]
// +- *SortMergeJoin [k#41], [k#46], Inner
// :- *Sort [k#41 ASC NULLS FIRST], false, 0
// : +- *Project [k#41, v#42]
// : +- *Filter isnotnull(k#41)
// : +- *FileScan parquet default.df1[k#41,v#42] Batched: true, Format: Parquet, Location: InMemoryFileIndex[file:/spark-warehouse/df1], PartitionFilters: [], PushedFilters: [IsNotNull(k)], ReadSchema: struct<k:string,v:int>
// +- *Sort [k#46 ASC NULLS FIRST], false, 0
// +- *Project [k#46, v2#47]
// +- *Filter isnotnull(k#46)
// +- *FileScan parquet default.df2[k#46,v2#47] Batched: true, Format: Parquet, Location: InMemoryFileIndex[file:/spark-warehouse/df2], PartitionFilters: [], PushedFilters: [IsNotNull(k)], ReadSchema: struct<k:string,v2:double>
* 파티션 레이아웃 이란 데이터 배포만을 의미합니다. partitioned
RDD에는 더 이상 파티 셔 너가 없습니다. ** 초기 예측이 없다고 가정합니다. 집계가 열의 작은 하위 집합 만 포함하는 경우에는 전혀 이득이 없습니다.