खैर, आपके डेटासेट को थोड़ा और दिलचस्प बनाने की सुविधा देता है:
val rdd = sc.parallelize(for {
x <- 1 to 3
y <- 1 to 2
} yield (x, None), 8)
हमारे छह तत्व हैं:
rdd.count
Long = 6
कोई पार्टीशनर नहीं:
rdd.partitioner
Option[org.apache.spark.Partitioner] = None
और आठ विभाजन:
rdd.partitions.length
Int = 8
अब प्रति विभाजन तत्वों की संख्या की गणना करने के लिए छोटे सहायक को परिभाषित करते हैं:
import org.apache.spark.rdd.RDD
def countByPartition(rdd: RDD[(Int, None.type)]) = {
rdd.mapPartitions(iter => Iterator(iter.length))
}
चूंकि हमारे पास विभाजनकर्ता नहीं है, इसलिए हमारे डेटासेट को विभाजन के बीच समान रूप से वितरित किया जाता है ( स्पार्क में डिफ़ॉल्ट विभाजन योजना ))
countByPartition(rdd).collect()
Array[Int] = Array(0, 1, 1, 1, 0, 1, 1, 1)
अब हमारे डेटा को पुनः आरंभ करने देता है:
import org.apache.spark.HashPartitioner
val rddOneP = rdd.partitionBy(new HashPartitioner(1))
चूंकि पैरामीटर HashPartitioner
विभाजन की संख्या को परिभाषित करता है, इसलिए हमें एक विभाजन की उम्मीद है:
rddOneP.partitions.length
Int = 1
चूंकि हमारे पास केवल एक विभाजन है, इसमें सभी तत्व शामिल हैं:
countByPartition(rddOneP).collect
Array[Int] = Array(6)
ध्यान दें कि फेरबदल के बाद मूल्यों का क्रम गैर-नियतात्मक है।
उसी तरह अगर हम उपयोग करते हैं HashPartitioner(2)
val rddTwoP = rdd.partitionBy(new HashPartitioner(2))
हम 2 विभाजन प्राप्त करेंगे:
rddTwoP.partitions.length
Int = 2
चूंकि rdd
कुंजी डेटा द्वारा विभाजित किया गया है, अब समान रूप से वितरित नहीं किया जाएगा:
countByPartition(rddTwoP).collect()
Array[Int] = Array(2, 4)
क्योंकि तीन चाबियों के साथ और केवल दो अलग-अलग वैल्यू hashCode
मॉड के साथ numPartitions
यहां कुछ भी अप्रत्याशित नहीं है:
(1 to 3).map((k: Int) => (k, k.hashCode, k.hashCode % 2))
scala.collection.immutable.IndexedSeq[(Int, Int, Int)] = Vector((1,1,1), (2,2,0), (3,3,1))
बस ऊपर की पुष्टि करने के लिए:
rddTwoP.mapPartitions(iter => Iterator(iter.map(_._1).toSet)).collect()
Array[scala.collection.immutable.Set[Int]] = Array(Set(2), Set(1, 3))
अंत में HashPartitioner(7)
हम सात विभाजन प्राप्त करते हैं, दो तत्वों के साथ तीन गैर-रिक्त:
val rddSevenP = rdd.partitionBy(new HashPartitioner(7))
rddSevenP.partitions.length
Int = 7
countByPartition(rddTenP).collect()
Array[Int] = Array(0, 2, 2, 2, 0, 0, 0)
सारांश और नोट्स
HashPartitioner
एक एकल तर्क लेता है जो विभाजन की संख्या को परिभाषित करता है
मान hash
कुंजियों का उपयोग करके विभाजन को दिए गए हैं । hash
फ़ंक्शन भाषा के आधार पर भिन्न हो सकता है (स्काला आरडीडी का उपयोग कर सकते हैं hashCode
, DataSets
मुरमुरैश 3, पायस्पार्क का उपयोग करें portable_hash
)।
इस तरह के सरल मामले में, जहां कुंजी एक छोटा पूर्णांक है, आप मान सकते हैं कि hash
यह एक पहचान ( i = hash(i)
) है।
स्काला एपीआई nonNegativeMod
गणना हैश के आधार पर विभाजन का निर्धारण करने के लिए उपयोग करता है,
यदि कुंजियों का वितरण एक समान नहीं है, तो आप उन स्थितियों में समाप्त हो सकते हैं जब आपके क्लस्टर का हिस्सा निष्क्रिय है
चाबियों को धोने योग्य होना चाहिए। आप PySpark के विशेष मुद्दों के बारे में पढ़ने के लिए PySpark को कम करने के लिए एक कुंजी के रूप में A सूची के लिए मेरे उत्तर की जांच कर सकते हैं। एक अन्य संभावित समस्या हशपार्टिशनर प्रलेखन द्वारा उजागर की गई है :
जावा सरणियों में हैशकोड होते हैं जो उनकी सामग्री के बजाय सरणियों की पहचान पर आधारित होते हैं, इसलिए एक RDD [Array [ ]] या RDD [(Array [ ], _)] को HashParter का उपयोग करके विभाजन का प्रयास एक अप्रत्याशित या गलत परिणाम उत्पन्न करेगा।
पायथन 3 में आपको यह सुनिश्चित करना होगा कि हैशिंग सुसंगत है। देखें क्या होता है अपवाद: स्ट्रिंग के हैश की यादृच्छिकता PYTHONHASHSEED के माध्यम से pyspark में होनी चाहिए?
हैश पार्टीशनर न तो इंजेक्टिव होता है और न ही स्पेशल। एकाधिक कुंजियों को एक ही विभाजन को सौंपा जा सकता है और कुछ विभाजन खाली रह सकते हैं।
कृपया ध्यान दें कि वर्तमान में हैश आधारित विधियाँ स्केल में काम नहीं करती हैं जब REPL परिभाषित केस क्लासेस ( अपाचे स्पार्क में केस क्लास समानता ) के साथ संयुक्त किया जाता है ।
HashPartitioner
(या कोई अन्य Partitioner
) डेटा फेरबदल करता है। जब तक कई कार्यों के बीच विभाजन का पुन: उपयोग नहीं किया जाता है, तब तक डेटा की मात्रा कम नहीं होती है।
(1, None)
के साथhash(2) % P
जहां पी विभाजन है। यह नहीं होना चाहिएhash(1) % P
?