स्पार्क में चरणों को कार्यों में कैसे विभाजित किया जाता है?


143

निम्नलिखित के लिए मान लें कि केवल एक स्पार्क नौकरी हर बिंदु पर चल रही है।

मुझे अब तक क्या मिला है

यहाँ मैं समझता हूँ कि स्पार्क में क्या होता है:

  1. जब एक SparkContextबनाया जाता है, तो प्रत्येक कार्यकर्ता नोड एक निष्पादक शुरू करता है। निष्पादक अलग-अलग प्रक्रियाएं (JVM) हैं, जो ड्राइवर प्रोग्राम में वापस कनेक्ट होती हैं। प्रत्येक निष्पादक के पास ड्राइवर प्रोग्राम का जार है। एक ड्राइवर छोड़ने, निष्पादकों को बंद कर देता है। प्रत्येक निष्पादक कुछ विभाजन पकड़ सकता है।
  2. जब एक नौकरी निष्पादित होती है, तो वंशावली ग्राफ के अनुसार एक निष्पादन योजना बनाई जाती है।
  3. निष्पादन कार्य को चरणों में विभाजित किया जाता है, जहां कई पड़ोसी (वंश ग्राफ में) परिवर्तन और कार्रवाई वाले चरण होते हैं, लेकिन कोई फेरबदल नहीं होता है। इस प्रकार चरणों को फेरबदल द्वारा अलग किया जाता है।

छवि 1

मैं समझता हूँ कि

  • एक कार्य एक कमांड है जो ड्राइवर से फंक्शन ऑब्जेक्ट को क्रमांकित करके एक निष्पादक को भेजा जाता है।
  • निष्पादक कमांडर (कार्य) के साथ डिसेरिअलाइज़ करता है और इसे एक पार्टीशन पर निष्पादित करता है।

परंतु

प्रशन)

मैं उन कार्यों में चरण कैसे विभाजित करूं?

विशेष रूप से:

  1. क्या परिवर्तन और कार्यों द्वारा निर्धारित कार्य हैं या एक कार्य में कई परिवर्तन / कार्य हो सकते हैं?
  2. विभाजन द्वारा निर्धारित कार्य हैं (उदाहरण के लिए प्रति विभाजन प्रति चरण एक कार्य)।
  3. क्या कार्य नोड द्वारा निर्धारित होते हैं (उदाहरण के लिए प्रति नोड प्रति चरण एक कार्य)?

मुझे क्या लगता है (केवल आंशिक उत्तर, भले ही सही)

Https : //0x0fff.com/spark-altecture-shuffle में , फेरबदल को छवि के साथ समझाया गया है

यहां छवि विवरण दर्ज करें

और मुझे यह आभास होता है कि नियम है

प्रत्येक चरण को नोड्स की संख्या के संबंध में # संख्या-विभाजन कार्यों में विभाजित किया गया है

अपनी पहली छवि के लिए मैं कहूंगा कि मेरे पास 3 मानचित्र कार्य होंगे और 3 कार्य कम करेंगे।

0x0fff से छवि के लिए, मैं कहूंगा कि 8 मानचित्र कार्य हैं और 3 कार्य कम करते हैं (यह मानते हुए कि केवल तीन नारंगी और तीन गहरे हरे रंग की फाइलें हैं)।

किसी भी मामले में प्रश्न खोलें

क्या वो सही है? लेकिन अगर यह सही है, तो भी, ऊपर दिए गए मेरे प्रश्नों का उत्तर नहीं दिया गया है, क्योंकि यह अभी भी खुला है, चाहे कई ऑपरेशन (जैसे कई नक्शे) एक कार्य के भीतर हों या प्रति ऑपरेशन एक कार्य में अलग हो गए हों।

दूसरे क्या कहते हैं

स्पार्क में एक कार्य क्या है? स्पार्क कार्यकर्ता जार फ़ाइल को कैसे निष्पादित करता है? और Apache स्पार्क शेड्यूलर फ़ाइलों को कार्यों में कैसे विभाजित करता है? समान हैं, लेकिन मुझे ऐसा नहीं लगा कि मेरे प्रश्न का उत्तर वहां स्पष्ट रूप से दिया गया था।

जवाबों:


52

तुम यहाँ एक बहुत अच्छी रूपरेखा है। अपने सवालों के जवाब देने के लिए

  • एक अलग task करता है प्रत्येक के लिए डेटा के प्रत्येक विभाजन के लिए शुरू होने की जरूरत है stage। इस बात पर विचार करें कि प्रत्येक विभाजन की संभावना अलग-अलग भौतिक स्थानों पर होगी - जैसे HDFS में ब्लॉक या स्थानीय फ़ाइल सिस्टम के लिए निर्देशिका / वॉल्यूम।

ध्यान दें कि Stages का सबमिशन किसके द्वारा संचालित है DAG Scheduler। इसका मतलब है कि जो चरण अन्योन्याश्रित नहीं हैं, उन्हें समानांतर में निष्पादन के लिए क्लस्टर में प्रस्तुत किया जा सकता है: यह क्लस्टर पर समानांतरकरण क्षमता को अधिकतम करता है। इसलिए यदि हमारे डेटाफ़्लो में ऑपरेशन एक साथ हो सकते हैं तो हम कई चरणों को लॉन्च करने की उम्मीद करेंगे।

हम देख सकते हैं कि निम्नलिखित खिलौना उदाहरण में कार्रवाई करते हैं जिसमें हम निम्नलिखित प्रकार के संचालन करते हैं:

  • दो डेटा स्रोत लोड करें
  • दोनों डेटा स्रोतों पर कुछ मानचित्र संचालन अलग से करें
  • उनके साथ जाओ
  • परिणाम पर कुछ मानचित्र और फ़िल्टर संचालन करें
  • परिणाम सहेजें

तो फिर हम कितने चरणों में समाप्त होंगे?

  • दो चरणों को समानांतर = 2 चरणों में लोड करने के लिए प्रत्येक चरण
  • एक तीसरे चरण का प्रतिनिधित्व joinकरता है निर्भर अन्य दो चरणों पर
  • ध्यान दें: सम्मिलित डेटा पर काम करने वाले सभी अनुवर्ती ऑपरेशन एक ही चरण में किए जा सकते हैं क्योंकि उन्हें क्रमिक रूप से होना चाहिए। अतिरिक्त चरणों को लॉन्च करने का कोई लाभ नहीं है क्योंकि वे तब तक काम शुरू नहीं कर सकते हैं जब तक कि पूर्व ऑपरेशन पूरा नहीं हो जाता।

यहाँ वह खिलौना कार्यक्रम है

val sfi  = sc.textFile("/data/blah/input").map{ x => val xi = x.toInt; (xi,xi*xi) }
val sp = sc.parallelize{ (0 until 1000).map{ x => (x,x * x+1) }}
val spj = sfi.join(sp)
val sm = spj.mapPartitions{ iter => iter.map{ case (k,(v1,v2)) => (k, v1+v2) }}
val sf = sm.filter{ case (k,v) => v % 10 == 0 }
sf.saveAsTextFile("/data/blah/out")

और यहां परिणाम का डीएजी है

यहां छवि विवरण दर्ज करें

अब: कितने कार्य ? कार्यों की संख्या बराबर होनी चाहिए

सम ( Stage* #Partitions in the stage)


2
धन्यवाद! कृपया मेरे पाठ के संबंध में अपना उत्तर विस्तृत करें: 1) क्या मेरे चरणों की परिभाषा व्यापक नहीं है? ऐसा लगता है कि मैंने आवश्यकता को याद किया कि एक चरण में ऐसे ऑपरेशन नहीं हो सकते जो समानांतर हो सकते हैं। या मेरा विवरण सख्ती से पहले से ही लागू है? 2) कार्य के लिए निष्पादित किए जाने वाले कार्यों की संख्या को विभाजन की संख्या से निर्धारित किया जाता है, लेकिन प्रोसेसर या नोड की संख्या नहीं, जबकि एक ही समय में निष्पादित किए जाने वाले कार्यों की संख्या पर निर्भर करता है प्रोसेसर, सही? 3) एक कार्य में कई ऑपरेशन हो सकते हैं?
Make42

1
4) आपके आखिरी वाक्य का क्या मतलब था? आखिरकार, संख्या विभाजन चरण से चरण तक भिन्न हो सकते हैं। क्या आपका मतलब यह है कि आपने सभी चरणों के लिए अपनी नौकरी को कैसे कॉन्फ़िगर किया है?
Make42

@ Make42 निश्चित रूप से विभाजन की संख्या एक चरण से दूसरे चरण में भिन्न हो सकती है - आप सही हैं। sum(..)उस भिन्नता को ध्यान में रखते हुए यह कहना मेरा उद्देश्य था ।
जावदबा

वाह, आपका जवाब पूरी तरह से ठीक था लेकिन दुर्भाग्य से, अंतिम वाक्य निश्चित रूप से एक गलत अवधारणा है। इसका मतलब यह नहीं है कि एक चरण में विभाजन संख्या प्रोसेसर की संख्या के बराबर है, हालांकि, आप अपनी मशीन पर प्रस्तुत कोर की संख्या के अनुसार एक RDD के लिए विभाजन की संख्या निर्धारित कर सकते हैं।
epcpu

@epcpu यह एक विशेष मामला था - लेकिन मैं मानता हूं कि यह भ्रामक होगा इसलिए मैं इसे हटा रहा हूं।
javadba

26

इससे आपको विभिन्न टुकड़ों को बेहतर ढंग से समझने में मदद मिल सकती है:

  • स्टेज: कार्यों का एक संग्रह है। समान प्रक्रिया डेटा (विभाजन) के विभिन्न सबसेट के खिलाफ चल रही है।
  • कार्य: एक वितरित डेटासेट के विभाजन पर कार्य की एक इकाई का प्रतिनिधित्व करता है। इसलिए प्रत्येक चरण में, संख्या-के-कार्य = संख्या-विभाजन, या जैसा कि आपने कहा था "विभाजन के अनुसार प्रति चरण एक कार्य"।
  • प्रत्येक निष्पादित एक यार्न कंटेनर पर चलता है, और प्रत्येक कंटेनर एक नोड पर रहता है।
  • प्रत्येक चरण कई निष्पादकों का उपयोग करता है, प्रत्येक निष्पादक को कई vcores आवंटित किए जाते हैं।
  • प्रत्येक vcore एक समय में ठीक एक कार्य निष्पादित कर सकता है
  • इसलिए किसी भी स्तर पर, कई कार्यों को समानांतर में निष्पादित किया जा सकता है। नंबर-ऑफ-टास्क रनिंग = नंबर-ऑफ-वीसीएस का उपयोग किया जा रहा है।

2
यह स्पार्क आर्किटेक्चर पर एक बहुत ही उपयोगी रीड है: 0x0fff.com/spark- Ernakulamecture
pedram bashiri

मुझे आपका पॉइंट नंबर 3 नहीं मिला। जहाँ तक मुझे पता है कि प्रत्येक नोड में कई निष्पादक हो सकते हैं, इसलिए बिंदु 3 के अनुसार: प्रति नोड केवल एक निष्पादक होना चाहिए। क्या आप इस बिंदु को स्पष्ट कर सकते हैं?
ऋतुपर्णो बेहरा

@RituparnoBehera प्रत्येक नोड में म्यूटेंट कंटेनर और इस प्रकार कई स्पार्क निष्पादक हो सकते हैं। इस लिंक को देखें। docs.cloudera.com/runtime/7.0.2/running-spark-applications/…
पेड्रम बशीरी

15

अगर मैं सही ढंग से समझता हूं कि आप को भ्रमित करने वाली 2 (संबंधित) चीजें हैं:

1) किसी कार्य की सामग्री क्या निर्धारित करती है?

2) निष्पादित किए जाने वाले कार्यों की संख्या क्या निर्धारित करती है?

स्पार्क का इंजन "glues" एक साथ लगातार rdds पर सरल संचालन, उदाहरण के लिए:

rdd1 = sc.textFile( ... )
rdd2 = rdd1.filter( ... )
rdd3 = rdd2.map( ... )
rdd3RowCount = rdd3.count

इसलिए जब rdd3 (lazily) की गणना की जाती है, तो स्पार्क rdd1 के विभाजन के अनुसार एक कार्य उत्पन्न करेगा और प्रत्येक कार्य rdd3 में परिणाम के लिए फ़िल्टर और मैप दोनों को प्रति पंक्ति निष्पादित करेगा।

कार्यों की संख्या विभाजन की संख्या से निर्धारित होती है। प्रत्येक RDD में विभाजन की एक निर्धारित संख्या होती है। एक स्रोत RDD के लिए जो HDFS (उदाहरण के लिए sc.textFile (... का उपयोग करके) से पढ़ा जाता है विभाजन की संख्या इनपुट प्रारूप द्वारा उत्पन्न विभाजन की संख्या है। RDD (s) पर कुछ संचालन अलग-अलग संख्या में विभाजन के साथ RDD में परिणाम कर सकते हैं:

rdd2 = rdd1.repartition( 1000 ) will result in rdd2 having 1000 partitions ( regardless of how many partitions rdd1 had ).

एक और उदाहरण जुड़ता है:

rdd3 = rdd1.join( rdd2  , numPartitions = 1000 ) will result in rdd3 having 1000 partitions ( regardless of partitions number of rdd1 and rdd2 ).

(अधिकांश) परिचालनों की संख्या बदलने वाले परिचालनों में फेरबदल होता है, जब हम उदाहरण के लिए करते हैं:

rdd2 = rdd1.repartition( 1000 ) 

वास्तव में क्या होता है rdd1 के प्रत्येक विभाजन पर कार्य एक अंतिम-आउटपुट का उत्पादन करने की आवश्यकता होती है जिसे निम्न चरण द्वारा पढ़ा जा सकता है ताकि rdd2 बनाने के लिए बिल्कुल 1000 विभाजन हों (वे इसे कैसे करते हैं? हैश या सॉर्ट करें )। इस ओर कार्य कभी-कभी "मैप (साइड) कार्यों" के रूप में संदर्भित होते हैं। एक कार्य जो बाद में rdd2 पर चलेगा, एक विभाजन पर कार्य करेगा (rdd2 का!) और यह पता लगाना होगा कि उस विभाजन से संबंधित मैप-साइड आउटपुट को कैसे पढ़ना / गठबंधन करना है। इस तरफ कार्य को कभी-कभी "कम (पक्ष) कार्य" के रूप में संदर्भित किया जाता है।

2 प्रश्न संबंधित हैं: एक चरण में कार्यों की संख्या विभाजनों की संख्या है (सामान्य से लगातार rdds "सरेस से जोड़ा हुआ") और एक rdd के विभाजन की संख्या चरणों के बीच बदल सकती है (कुछ विभाजनों की संख्या निर्दिष्ट करके) उदाहरण के लिए ऑपरेशन के कारण फेरबदल)।

एक बार एक मंच के निष्पादन शुरू होने पर, इसके कार्य टास्क स्लॉट पर कब्जा कर सकते हैं। समवर्ती कार्य-स्लॉट की संख्या numExecutors * ExecutorCores है। सामान्य तौर पर, इन पर विभिन्न, गैर-निर्भर चरणों के कार्यों द्वारा कब्जा किया जा सकता है।

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