स्पार्क> = 2.3.0
स्पार्क -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
स्पार्क> = 1.6 में क्वेरी और कैशिंग के लिए कॉलम द्वारा विभाजन का उपयोग करना संभव है। देखें: SPARK-11410 और SPARK-4849repartition
विधि का उपयोग कर :
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
स्पार्क के विपरीत Dataset
( Dataset[Row]
उर्फ सहित DataFrame
) अब के लिए कस्टम विभाजन का उपयोग नहीं कर सकते। आप आमतौर पर एक कृत्रिम विभाजन कॉलम बनाकर उसे संबोधित कर सकते हैं लेकिन यह आपको उतना लचीलापन नहीं देगा।
स्पार्क <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
)
इसलिए ऐसा लग रहा है कि यह असंभव नहीं है। यदि यह समझ में आता है तो यह सवाल बना रहता है। मैं तर्क दूंगा कि ज्यादातर समय यह नहीं होता है:
पुनर्मूल्यांकन एक महंगी प्रक्रिया है। एक विशिष्ट परिदृश्य में अधिकांश डेटा को क्रमबद्ध, फेरबदल और deserialized किया जाना होता है। दूसरी ओर से ऑपरेशनों की संख्या जो कि एक पूर्व-विभाजित डेटा से लाभ उठा सकती है, अपेक्षाकृत कम है और इस तक सीमित है यदि आंतरिक एपीआई को इस संपत्ति का लाभ उठाने के लिए डिज़ाइन नहीं किया गया है।
- कुछ परिदृश्यों में शामिल होता है, लेकिन इसके लिए आंतरिक समर्थन की आवश्यकता होती है,
- विंडो फंक्शंस मैचिंग पार्टीशनर के साथ कॉल करते हैं। ऊपर के रूप में ही, एक एकल खिड़की परिभाषा तक सीमित। यह पहले से ही आंतरिक रूप से विभाजित है, लेकिन पूर्व विभाजन निरर्थक हो सकता है,
- के साथ सरल एकत्रीकरण
GROUP BY
- यह अस्थायी बफ़र्स ** की स्मृति पदचिह्न को कम करना संभव है, लेकिन समग्र लागत बहुत अधिक है। कमोबेश groupByKey.mapValues(_.reduce)
(वर्तमान व्यवहार) बनामreduceByKey
(पूर्व विभाजन)। व्यवहार में उपयोगी होने के लिए अयोग्य।
- के साथ डेटा संपीड़न
SqlContext.cacheTable
। चूंकि ऐसा लगता है कि यह रन लंबाई एन्कोडिंग का उपयोग कर रहा है, इसलिए आवेदन OrderedRDDFunctions.repartitionAndSortWithinPartitions
संपीड़न अनुपात में सुधार कर सकता है।
प्रदर्शन कुंजी के वितरण पर अत्यधिक निर्भर है। यदि इसे तिरछा किया जाता है, तो यह एक उप-स्तरीय संसाधन उपयोग के परिणामस्वरूप होगा। सबसे खराब स्थिति में नौकरी खत्म करना असंभव होगा।
- एक उच्च स्तरीय घोषणात्मक एपीआई का उपयोग करने का एक पूरा बिंदु खुद को निम्न स्तर के कार्यान्वयन विवरण से अलग करना है। जैसा कि पहले ही @dwysakowicz और @RomiKuntsman द्वारा उल्लिखित एक अनुकूलन एक काम है कैटालिस्ट ऑप्टिमाइज़र । यह एक बहुत ही परिष्कृत जानवर है और मुझे वास्तव में संदेह है कि आप आसानी से उस पर सुधार कर सकते हैं, बिना इसके इंटर्नल में अधिक गहराई तक।
संबंधित अवधारणाएँ
जेडीबीसी स्रोतों के साथ विभाजन :
JDBC डेटा स्रोत predicates
तर्क का समर्थन करते हैं । इसका उपयोग इस प्रकार किया जा सकता है:
sqlContext.read.jdbc(url, table, Array("foo = 1", "foo = 3"), props)
यह प्रति विधेय के लिए एक एकल JDBC विभाजन बनाता है। ध्यान रखें कि यदि व्यक्तिगत विधेय का उपयोग करके बनाए गए सेट असंतुष्ट नहीं हैं, तो आप परिणामी तालिका में डुप्लिकेट देखेंगे।
partitionBy
में विधि DataFrameWriter
:
स्पार्क विधि 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 का अब कोई पार्टीशनर नहीं है। ** कोई प्रारंभिक प्रक्षेपण नहीं है। यदि एकत्रीकरण केवल स्तंभों के छोटे उपसमूह को कवर करता है तो संभवतः कोई लाभ नहीं है।