कैसे काम करता है HashPartitioner?


82

मैं के प्रलेखन पर पढ़ा HashPartitioner। दुर्भाग्य से एपीआई कॉल के अलावा ज्यादा कुछ नहीं बताया गया था। मैं इस धारणा के तहत हूं कि HashPartitionerकुंजी के हैश के आधार पर वितरित सेट विभाजन। उदाहरण के लिए अगर मेरा डेटा पसंद है

(1,1), (1,2), (1,3), (2,1), (2,2), (2,3)

इसलिए विभाजनकर्ता इसे अलग-अलग विभाजनों में डालता है, जिसमें एक ही विभाजन में एक ही कुंजी गिरती है। हालाँकि मुझे कंस्ट्रक्टर के तर्क की अहमियत समझ में नहीं आती

new HashPartitoner(numPartitions) //What does numPartitions do?

उपरोक्त डेटासेट के लिए अगर मैंने किया तो परिणाम कैसे भिन्न होंगे

new HashPartitoner(1)
new HashPartitoner(2)
new HashPartitoner(10)

तो HashPartitionerवास्तव में कैसे काम करता है ?

जवाबों:


162

खैर, आपके डेटासेट को थोड़ा और दिलचस्प बनाने की सुविधा देता है:

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)

inital-वितरण

अब हमारे डेटा को पुनः आरंभ करने देता है:

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)

हैश-विभाजक -1

ध्यान दें कि फेरबदल के बाद मूल्यों का क्रम गैर-नियतात्मक है।

उसी तरह अगर हम उपयोग करते हैं 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))

हैश-विभाजक -2

अंत में 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)

हैश-विभाजक-7

सारांश और नोट्स

  • 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?
javamonkey79

स्पार्क 2.2 का उपयोग कर Im, और partitionByrdd में कोई एपीआई नहीं है । डेटाफ्रेम.राइट के तहत एक पार्टीशन होता है, लेकिन यह पार्टीशनर को तर्क के रूप में नहीं लेता है।
हकुनामी

awsome उत्तर ... फेरबदल partioner पर आधारित है, एक अच्छा partioner फेरबदल डेटा राशि को कम कर सकता है।
लियोन

1
महान 👌 जवाब। मेरे पास एक सवाल है जिसका मुझे इंटरनेट पर ठोस जवाब नहीं मिल रहा है। जब हम df.repartition (n) Ie का उपयोग करते हैं, जब हम किसी भी कॉलम को एक कुंजी के रूप में निर्दिष्ट नहीं करते हैं, तो हैश फंक्शन वर्कर्स आंतरिक रूप से कैसे करते हैं क्योंकि इसमें कुछ भी हैशेड नहीं है?
dsk

@dsk, यदि आप एक कुंजी निर्दिष्ट नहीं करते हैं, तो मेरा मानना ​​है कि रिपर्टिशन राउंडरोबिनपार्टिशनिंग का उपयोग करता है। कुछ चर्चा यहाँ
माइक सूडर

6

RDDवितरित किया जाता है इसका मतलब यह है कि यह कुछ भागों में विभाजित है। इस विभाजन में से प्रत्येक अलग मशीन पर संभावित है। तर्क के साथ हैश विभाजनकर्ता निम्न तरीके से numPartitionsजोड़ी को किस विभाजन पर रखता है (key, value):

  1. बिल्कुल numPartitionsविभाजन बनाता है ।
  2. (key, value)संख्या के साथ विभाजन में स्थानHash(key) % numPartitions

3

HashPartitioner.getPartitionविधि एक लेता है कुंजी अपने तर्क के रूप में और रिटर्न सूचकांक विभाजन जो कुंजी के अंतर्गत आता है की। विभाजनकर्ता को यह जानना होता है कि वैध सूचकांक क्या हैं, इसलिए यह सही सीमा में संख्या देता है। विभाजक numPartitionsतर्क के माध्यम से विभाजन की संख्या निर्दिष्ट की गई है।

कार्यान्वयन मोटे तौर पर लौटता है key.hashCode() % numPartitions। अधिक विवरण के लिए Partitioner.scala देखें ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.