प्रतिनिधि इंटरव्यू विषय

डेटा इंजीनियरिंग साक्षात्कार: आप Spark Data Skew का निदान और समाधान कैसे करते हैं?

डेटाकठिन
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

एक दैनिक Spark SQL जॉब 4.8 TB की इवेंट फैक्ट टेबल को 180 GB के प्रोडक्ट डायमेंशन के साथ left-join करती है, जहाँ product_id='UNKNOWN' पर 32% इवेंट्स मैप होते हैं (अपस्ट्रीम रिलीज़ के बाद)। 2,000 शफल पार्टीशन्स में, मीडियन टास्क शफल रीड 1.1 GiB है, लेकिन एक टास्क 720 GiB रीड करता है, बार-बार डिस्क पर स्पिल होता है, और OOM के साथ फेल हो जाता है; रनटाइम 24 से बढ़कर 96 मिनट हो जाता है। आप कारण को कैसे साबित करेंगे, डेटा हटाए बिना या जॉइन परिणाम बदले बिना इसे कैसे ठीक करेंगे, और समाधान को कैसे मान्य करेंगे?

Prompt और यह कब लागू होता है

एक दैनिक Spark SQL जॉब product_id पर 4.8 TB इवेंट फैक्ट टेबल को 180 GB प्रोडक्ट डायमेंशन के साथ left-join करती है। एक अपस्ट्रीम रिलीज़ के बाद, 32% इवेंट्स product_id='UNKNOWN' पर सामान्यीकृत (normalized) हो जाते हैं। जॉब 2,000 शफल पार्टीशन्स का उपयोग करती है। जॉइन स्टेज में, Spark UI 1.1 GiB का मीडियन टास्क शफल रीड दिखाता है, जबकि एक टास्क 720 GiB रीड करता है, बार-बार डिस्क पर स्पिल करता है, और अंततः पुनः प्रयासों (retries) के बाद OOM के साथ विफल हो जाता है। रनटाइम 24 से बढ़कर 96 मिनट हो गया है। व्यवसाय की आवश्यकता है कि अज्ञात-उत्पाद (unknown-product) इवेंट्स को छोड़े बिना या left-join परिणाम को बदले बिना जॉब 45 मिनट के भीतर समाप्त हो जाए।

टेबल का आकार, की का अनुपात (key share), पार्टीशन मेट्रिक्स, रनटाइम और SLA साक्षात्कार की मान्यताएँ हैं। मुख्य कार्य पार्टीशन-स्तरीय साक्ष्यों का उपयोग करके डेटा स्कीव (data skew) को अपर्याप्त संसाधनों और जॉइन-आउटपुट विस्फोट (output explosion) से अलग करना है, फिर ऐसा उपाय चुनना है जो डेटा अनुबंध (data contract) को बनाए रखे। यह डेटा श्रेणी से संबंधित है क्योंकि यह Spark निष्पादन योजनाओं (execution plans), शफल पार्टीशन्स, डेटा वितरण और बैच शुद्धता का परीक्षण करता है। मौजूदा Kafka हॉट-पार्टीशन प्रश्न मैसेज कीज़, ऑर्डरिंग और उपभोक्ता ऑफ़सेट पर केंद्रित है। यह प्रश्न रनटाइम SQL पार्टीशन्स, जॉइन रणनीति, AQE और परिणाम संरक्षण पर केंद्रित है, इसलिए विफलता स्तर और सत्यापन विधि अलग हैं।

यही तर्क groupBy, distinct, विंडो फ़ंक्शंस और अन्य वाइड डिपेंडेंसीज़ (wide dependencies) पर भी लागू होता है। जब एक की (key) के कई रिकॉर्ड कुछ पोस्ट-शफल टास्क्स पर एकत्रित हो जाते हैं, तो वे स्ट्रैगलर (stragglers), स्पिल, GC दबाव, या OOM उत्पन्न कर सकते हैं। एक अच्छा उत्तर मेमोरी बढ़ाकर शुरू नहीं होता है। यह पहले यह साबित करता है कि क्या सबसे धीमा टास्क डेटा और कम्प्यूटेशन की अनुपातहीन मात्रा का स्वामी है।

साक्षात्कारकर्ता क्या मूल्यांकन करता है

साक्ष्य की ग्रैन्युलैरिटी (granularity) से शुरुआत करें। एक मजबूत उत्तर जॉब से SQL क्वेरी की ओर, फिर एक विशिष्ट स्टेज और उसके व्यक्तिगत टास्क्स की ओर बढ़ता है। यह अवधि (duration), शफल-रीड रिकॉर्ड्स और बाइट्स, स्पिल, पीक एक्ज़ीक्यूशन मेमोरी और GC समय की तुलना करता है। एक टास्क जो मीडियन से सैकड़ों गुना अधिक रीड करता है और दूसरे एक्ज़ीक्यूटर पर पुनः प्रयास करने पर भी धीमा रहता है, वह नियतात्मक डेटा स्कीव (deterministic data skew) का समर्थन करता है। केवल कुल एक्ज़ीक्यूटर मेमोरी और कुल रनटाइम उस कारण को स्थापित नहीं करते हैं।

एक्ज़ीक्यूशन-प्लान की समझ भी उतनी ही महत्वपूर्ण है। EXPLAIN FORMATTED Exchange, जॉइन प्रकार, और फिजिकल जॉइन की पुष्टि करता है। EXPLAIN COST और SQL UI में रनटाइम आँकड़े अनुमानित और देखे गए डेटा आकार को उजागर करते हैं। Spark AQE योजना को समायोजित करने के लिए रनटाइम आँकड़ों का उपयोग करता है। Spark 4.2.0 में, skew-join ऑप्टिमाइज़ेशन एक स्कीव्ड सॉर्ट-मर्ज-जॉइन पार्टीशन को विभाजित कर सकता है और आवश्यकता पड़ने पर छोटे पक्ष की प्रतिकृति (replicate) बना सकता है। प्रलेखित डिफ़ॉल्ट किसी पार्टीशन को स्कीव्ड तभी चिह्नित करते हैं जब वह मीडियन के पाँच गुना और 256 MiB दोनों से अधिक हो। एक उम्मीदवार को प्रभावी वातावरण कॉन्फ़िगरेशन का निरीक्षण करना चाहिए क्योंकि दस्तावेज़ीकरण डिफ़ॉल्ट्स अपरिवर्तनीय क्लस्टर तथ्य नहीं हैं।

डेटा सिमेंटिक्स (Data semantics) विभाजक रेखा बन जाते हैं। UNKNOWN एक मान्य "अनएट्रिब्यूटेड" (unattributed) इवेंट हो सकता है या एक अपस्ट्रीम दोष। उन पंक्तियों को हटाना, यादृच्छिक रूप से वितरित करना, या फिर से लिखना परिणाम को बदल सकता है। एक सॉल्टेड जॉइन को केवल डायमेंशन से हॉट-की पंक्तियों की प्रतिकृति बनानी चाहिए और हॉट फैक्ट पंक्तियों को एक नियतात्मक सॉल्ट (deterministic salt) असाइन करना चाहिए। पूरे डायमेंशन की प्रतिकृति बनाने से डेटा वॉल्यूम कई गुना बढ़ जाता है, जबकि दोनों पक्षों को स्वतंत्र रूप से रैंडमाइज़ करने से मैच छूट जाते हैं।

उपाय का चयन गहराई के एक अन्य स्तर को प्रकट करता है। spark.sql.shuffle.partitions बढ़ाने से अधिक हैश बकेट्स बनते हैं, लेकिन एक हॉट की की प्रत्येक पंक्ति अभी भी एक ही बकेट में जाती है। ब्रॉडकास्ट केवल तभी उपयुक्त होता है जब प्रोजेक्शन, फ़िल्टरिंग, और विश्वसनीय आँकड़े यह साबित करते हैं कि एक पक्ष हर एक्ज़ीक्यूटर पर सुरक्षित रूप से फिट बैठता है; 180 GB डायमेंशन को ब्रॉडकास्ट करने के लिए मजबूर करना असुरक्षित है। AQE कम दखल वाला पहला विकल्प है। जब AQE ट्रिगर नहीं होता है या फिर भी SLA छूट जाता है, तो स्थिर हॉट कीज़ के लिए स्पष्ट सॉल्टिंग (explicit salting) उपयुक्त होती है। एग्रीगेशंस अक्सर सॉल्टेड आंशिक एग्रीगेशन के बाद एक दूसरे मर्ज का उपयोग करते हैं।

पूर्ण सत्यापन उत्तर को समाप्त करता है। एक प्रदर्शन समाधान को यह भी साबित करना होगा कि पंक्ति गणना, व्यावसायिक राशियाँ, अज्ञात कीज़, बेमेल दरें (unmatched rates) और डुप्लिकेट दरें अपरिवर्तित हैं। रनटाइम को 96 से घटाकर 40 मिनट करना शुद्धता साबित नहीं करता है या यह नहीं दिखाता है कि समाधान कल के किसी भिन्न की (key) वितरण में जीवित रहेगा या नहीं।

उत्तर देने से पहले स्पष्ट करने योग्य प्रश्न

  • क्या बाधा स्कैन, शफल राइट, या शफल रीड के बाद है? असमान स्कैन टास्क्स विशाल या गैर-विभाज्य (unsplittable) फ़ाइलों से आ सकते हैं। यह प्रॉम्प्ट की-स्कीव पथ तक पहुँचता है क्योंकि आउटलायर जॉइन शफल के बाद दिखाई देता है।
  • क्या 32% रिकॉर्ड्स, कंप्रेस्ड बाइट्स, या प्रोसेसिंग लागत को संदर्भित करता है? वाइड पंक्तियाँ, महंगे UDFs और आउटपुट फैन-आउट लागत स्कीव बना सकते हैं, भले ही पंक्ति गणना मामूली दिखे। रिकॉर्ड्स, बाइट्स, समय और आउटपुट पंक्तियों की तुलना करें।
  • व्यवसाय के लिए UNKNOWN का क्या अर्थ है? यदि अज्ञात उत्पादों को किसी डायमेंशन विशेषता की आवश्यकता नहीं है, तो उन्हें मुख्य जॉइन से अलग करें और मूल अनुबंध के तहत नल (null) विशेषताएँ भरें। यदि उन्हें एक सेंटिनल डायमेंशन पंक्ति से मेल खाना चाहिए, तो जॉइन को बनाए रखें और हॉटस्पॉट को विभाजित करें।
  • क्या प्रोडक्ट डायमेंशन में product_id अद्वितीय (unique) है? कई UNKNOWN डायमेंशन पंक्तियाँ हॉट फैक्ट की को मैनी-टू-मैनी (many-to-many) आउटपुट विस्फोट में बदल देती हैं। ट्यूनिंग से पहले खराब कार्डिनैलिटी को पार्टीशन स्कीव से अलग करें।
  • कौन सा Spark संस्करण और कौन सी AQE सेटिंग्स प्रभावी हैं? स्विच, थ्रेसहोल्ड, जॉइन प्रकार और स्कीव स्प्लिटिंग वास्तव में चलने के प्रमाण के लिए पर्यावरण (Environment) पृष्ठ और अंतिम अनुकूली योजना (adaptive plan) की जाँच करें।
  • क्या 180 GB मूल डायमेंशन है या इसका प्रोजेक्टेड जॉइन इनपुट है? यदि की (key) और दो विशेषताओं पर फ़िल्टर करने से यह सुरक्षित रूप से ब्रॉडकास्ट करने योग्य बन जाता है, तो ब्रॉडकास्ट टू-साइडेड शफल को पछाड़ सकता है। रनटाइम आँकड़ों और एक्ज़ीक्यूटर-मेमोरी बजट को उस मामले को साबित करना होगा।
  • कौन से इनवेरिएंट्स (invariants) एक समतुल्य परिणाम को परिभाषित करते हैं? न्यूनतम रूप से, कुल पंक्तियाँ, अद्वितीय इवेंट्स, एडिटिव व्यावसायिक माप, अज्ञात-की पंक्तियाँ, बेमेल पंक्तियाँ और अनुमत डुप्लिकेट सिमेंटिक्स निर्दिष्ट करें।
  • क्या हॉट कीज़ स्थिर और गणना योग्य (enumerable) हैं? कुछ स्थिर कीज़ लक्षित सॉल्टिंग के अनुकूल होती हैं। बदलती हुई लंबी पूंछ (long tail) AQE, डायनेमिक हॉट-की डिटेक्शन, या अपस्ट्रीम सिमेंटिक सुधार का पक्ष लेती है।

30-सेकंड उत्तर ढांचा

"मैं SQL UI में जॉइन-टास्क शफल रीड, स्पिल, GC और पुनः प्रयास स्थान की तुलना करूँगा। 1.1 GiB मीडियन के मुकाबले 720 GiB का टास्क, साथ ही 32% UNKNOWN, की-स्कीव का समर्थन करता है; आउटपुट विस्फोट को बाहर करने के लिए मैं डायमेंशन विशिष्टता (uniqueness) को भी सत्यापित करूँगा। सबसे पहले मैं पुष्टि करूँगा कि AQE skew join बड़े पार्टीशन को विभाजित करता है। यदि रनटाइम अभी भी 45 मिनट से अधिक है, तो मैं स्थिर event_id द्वारा UNKNOWN को नियतात्मक रूप से सॉल्ट करूँगा और केवल इसकी सेंटिनल पंक्ति की प्रतिकृति बनाऊँगा। अंत में, मैं बेसलाइन के साथ पंक्तियों और राशियों का मिलान करूँगा, फिर अधिकतम-से-मीडियन टास्क इनपुट, स्टेज समय, और लागत की तुलना करूँगा।"

चरण-दर-चरण गहन उत्तर

चरण 1: 96 मिनट को एक स्टेज और टास्क के लिए जिम्मेदार ठहराएं

इवेंट लॉग को बनाए रखें और समान डेटा तिथि के लिए सामान्य और रीग्रेस्ड रन की तुलना करने के लिए Spark History Server का उपयोग करें। SQL क्वेरी का उसके स्टेज विवरणों में अनुसरण करें। शफल-रीड रिकॉर्ड्स और बाइट्स, शफल स्पिल, पीक एक्ज़ीक्यूशन मेमोरी, GC समय, विफलता कारण और एक्ज़ीक्यूटर के साथ संरेखित, पर्सेंटाइल और अधिकतम टास्क अवधि रिकॉर्ड करें। SQL योजना में, left join से पहले Exchange hashpartitioning(product_id, 2000) की जाँच करें, अंतिम फिजिकल जॉइन की पहचान करें, और निर्धारित करें कि क्या अनुकूली योजना (adaptive plan) पूरी हो गई थी।

यहाँ, दूसरे एक्ज़ीक्यूटर पर पुनः प्रयास करने के बाद भी वही टास्क लगभग 720 GiB रीड करता है, जबकि अधिकांश टास्क लगभग 1.1 GiB रीड करते हैं। यह इनपुट पार्टीशन की ओर ही इशारा करता है। यदि किसी धीमे टास्क में एक एक्ज़ीक्यूटर पर सामान्य इनपुट और उच्च GC या डिस्क प्रतीक्षा है, तो पहले नोड की जाँच करें। यदि प्रत्येक टास्क समान रूप से स्पिल होता है, तो समग्र पार्टीशन आकार निर्धारण और संसाधन बजट पर ध्यान केंद्रित करें। यदि आउटपुट पंक्तियाँ अचानक बढ़ जाती हैं, तो डायमेंशन डुप्लिकेट्स और जॉइन स्थिति का निरीक्षण करें।

चरण 2: की (key) वितरण और जॉइन कार्डिनैलिटी के साथ कारण साबित करें

उत्पादन द्वारा उपयोग किए जाने वाले बिल्कुल समान फ़िल्टर और की नॉर्मलाइज़ेशन को लागू करने के बाद हॉट कीज़ को मापें। कच्चे कॉलम का निरीक्षण करना अपर्याप्त है क्योंकि trim, केस नॉर्मलाइज़ेशन, coalesce, या एक UDF कई मानों को एक की (key) में समेट सकता है। एक बहुत बड़ी टेबल पर, मौजूदा आँकड़ों, एक नियंत्रित नमूने (sample), या एक सीमित एग्रीगेशन का उपयोग करें ताकि नैदानिक (diagnostic) स्वयं एक और अप्रतिबंधित जॉब न बन जाए। नीचे दी गई क्वेरी मानती है कि फैक्ट टेबल में पहले से ही payload_bytes शामिल है या पूर्व-गणना की गई है, जिससे पंक्ति और बाइट दोनों स्कीव को मापा जा सकता है। उस कॉलम के बिना, स्टोरेज आँकड़ों या नियंत्रित सीरियलाइज़ेशन अनुमान का उपयोग करें। क्वेरी आवश्यक गणना को व्यक्त करती है:

sql
SELECT
  COALESCE(product_id, '<NULL>') AS join_key,
  COUNT(*) AS row_count,
  SUM(payload_bytes) AS payload_bytes
FROM fact_events
WHERE event_date = DATE '2026-07-17'
GROUP BY COALESCE(product_id, '<NULL>')
ORDER BY row_count DESC
LIMIT 20;

यह भी साबित करें कि प्रत्येक product_id डायमेंशन में अधिकतम एक बार दिखाई देता है और जॉइन से पहले और बाद में फैक्ट-इवेंट गणनाओं की तुलना करें। इस left join के लिए, एक अद्वितीय डायमेंशन की का अर्थ है कि प्रत्येक फैक्ट इवेंट ठीक एक पंक्ति उत्पन्न करता है, जिसमें बेमेल इवेंट्स भी शामिल हैं। यदि UNKNOWN के पास फैक्ट पंक्तियों का 32% हिस्सा है, डायमेंशन में एक सेंटिनल पंक्ति है, और आउटपुट गुणा नहीं होता है, तो हैश पार्टीशनिंग एकल 720 GiB पार्टीशन की व्याख्या करती है।

चरण 3: एक्ज़ीक्यूशन तकनीक चुनने से पहले सिमेंटिक कारण को हल करें

जाँच करें कि अपस्ट्रीम रिलीज़ 32% इवेंट्स को UNKNOWN पर क्यों मैप करता है। यदि यह एक रीग्रेशन है, तो मैपिंग को रोल बैक या ठीक करें और प्रभावित पार्टीशन्स को फिर से चलाएँ। यह डेटा गुणवत्ता और प्रदर्शन दोनों को पुनर्स्थापित करता है। यदि यह एक मान्य व्यावसायिक मान है, तो निष्पादन परत को वितरण का समर्थन करना चाहिए।

मान्य अज्ञात इवेंट्स जिन्हें किसी उत्पाद विशेषता की आवश्यकता नहीं है, वे एक अलग रास्ता ले सकते हैं: कोल्ड कीज़ को सामान्य रूप से जॉइन करें, मौजूदा अनुबंध के तहत अज्ञात पथ के लिए डायमेंशन विशेषताओं को नल (null) से भरें, और unionByName के साथ संयोजित करें। यह बिना किसी सूचना लाभ के एक शफल को हटा देता है। यदि UNKNOWN को सेंटिनल विशेषताओं से मेल खाना चाहिए, तो जॉइन को बनाए रखें और AQE या लक्षित सॉल्टिंग के साथ कार्य को विभाजित करें। परिणाम सिमेंटिक्स शाखा का निर्णय करते हैं; एकसमान पार्टीशन डेटा को हटाने की अनुमति नहीं हैं।

चरण 4: सबसे कम खर्चीला उपाय चुनें जो काम करे

पहले AQE की जाँच करें। Spark 4.2.0 में, spark.sql.adaptive.enabled और spark.sql.adaptive.skewJoin.enabled डिफ़ॉल्ट रूप से सक्षम हैं, लेकिन एक क्लस्टर, जॉब या प्रबंधित प्लेटफ़ॉर्म उन्हें ओवरराइड कर सकता है। स्कीव-जॉइन फ़ैक्टर और एब्सोल्यूट-बाइट थ्रेसहोल्ड दोनों का मेल होना चाहिए। स्कीव हैंडलिंग के लिए अंतिम अनुकूली योजना का निरीक्षण करें और पुष्टि करें कि स्टेज मेट्रिक्स बड़े पार्टीशन विभाजन को दिखाते हैं। एक संगत सॉर्ट-मर्ज जॉइन के लिए, AQE बड़े पक्ष को विभाजित कर सकता है और छोटे पक्ष की प्रतिकृति बना सकता है। यह बदलते दैनिक वितरणों के अनुकूल होता है, लेकिन शफल और प्रतिकृति लागत जोड़ सकता है, और यह तार्किक रूप से गलत मैनी-टू-मैनी जॉइन की मरम्मत नहीं कर सकता है।

यदि प्रोजेक्टेड डायमेंशन में विश्वसनीय आँकड़े हैं और वह वास्तव में छोटा है, तो ब्रॉडकास्ट हैश जॉइन का मूल्यांकन करें ताकि फैक्ट साइड जॉइन की पर शफल न हो। मूल 180 GB सामान्य ब्रॉडकास्ट बजट से बहुत बाहर है। एक ज़बरदस्ती दिया गया हिंट (forced hint) हर एक्ज़ीक्यूटर को समाप्त (exhaust) कर सकता है। प्रोजेक्टेड बाइट्स, समवर्ती टास्क्स, एक्ज़ीक्यूटर हीप और ब्रॉडकास्ट टाइमआउट का एक साथ आकलन करें।

जब AQE ट्रिगर नहीं होता है या फिर भी SLA छूट जाता है, तो स्थिर हॉट कीज़ को मैन्युअल रूप से सॉल्ट करें। निम्नलिखित कोड मानता है कि event_id स्थिर और अद्वितीय है, product_id डायमेंशन में अद्वितीय है, और केवल UNKNOWN को विभाजित करने की आवश्यकता है। हॉट फैक्ट पंक्तियाँ 32 सॉल्ट्स में नियतात्मक रूप से मैप होती हैं। केवल मिलान करने वाली सेंटिनल डायमेंशन पंक्ति को 32 बार दोहराया जाता है। कोल्ड कीज़ सॉल्ट 0 पर रहती हैं, इसलिए पूर्ण डायमेंशन कभी भी कई गुना नहीं होता है।

python
from pyspark.sql import functions as F

SALT_BUCKETS = 32
HOT_KEYS = ["UNKNOWN"]

events_salted = events.withColumn(
    "salt",
    F.when(
        F.col("product_id").isin(*HOT_KEYS),
        F.pmod(F.xxhash64("event_id"), F.lit(SALT_BUCKETS)).cast("int"),
    ).otherwise(F.lit(0)),
)

salt_values = spark.range(SALT_BUCKETS).select(
    F.col("id").cast("int").alias("salt")
)

products_hot = (
    products.filter(F.col("product_id").isin(*HOT_KEYS))
    .crossJoin(salt_values)
)
products_cold = (
    products.filter(~F.col("product_id").isin(*HOT_KEYS))
    .withColumn("salt", F.lit(0))
)
products_salted = products_cold.unionByName(products_hot)

result = (
    events_salted.join(products_salted, ["product_id", "salt"], "left")
    .drop("salt")
)

इस साक्षात्कार परिदृश्य के तहत बत्तीस एक प्रारंभिक उम्मीदवार है। हॉट-पार्टीशन बाइट्स, लक्ष्य टास्क आकार, उपलब्ध समानांतरता (parallelism) और छोटे पक्ष की प्रतिकृति लागत से बकेट गणना प्राप्त करें, फिर प्रतिनिधि डेटा पर परीक्षण करें। बहुत कम बकेट्स एक लंबी पूंछ को बनाए रखते हैं। बहुत अधिक शेड्यूलिंग, फ़ाइल और प्रतिकृति ओवरहेड जोड़ते हैं। केवल repartition(4000, "product_id") चलाने से अभी भी प्रत्येक UNKNOWN पंक्ति एक पार्टीशन में जाती है।

groupBy(product_id) के लिए, आमतौर पर दोहराने के लिए कोई डायमेंशन नहीं होता है। पहले (product_id, salt) द्वारा आंशिक रूप से एग्रीगेट करें, फिर आंशिक परिणामों को product_id द्वारा मर्ज करें। संचालन जिन्हें साहचर्य (associative) और क्रमविनिमेय (commutative) मर्ज के साथ सुरक्षित रूप से विघटित किया जा सकता है, जैसे कि sum, count, min, और max, इस तकनीक में फिट बैठते हैं। सटीक median, ऑर्डर-डिपेंडेंट एग्रीगेशन, और गैर-मर्ज करने योग्य UDF स्थिति के लिए एक अलग एल्गोरिदम की आवश्यकता होती है।

चरण 5: शुद्धता, प्रदर्शन और लागत को एक स्वीकृति गेट में रखें

एक अपरिवर्तनीय इनपुट स्नैपशॉट के विरुद्ध बेसलाइन और उम्मीदवार को चलाएं। शुद्धता पहले आती है: कुल आउटपुट पंक्तियों, अद्वितीय event_id, UNKNOWN पंक्तियों, बेमेल पंक्तियों, और सार्थक डायमेंशन्स द्वारा व्यावसायिक योगों और गणनाओं की तुलना करें। हॉट कीज़, कोल्ड कीज़, नल और डुप्लिकेट डायमेंशन कीज़ के लिए पंक्ति-स्तरीय अंतर (diffs) लें। यह गारंटी कि एक फैक्ट इवेंट एक left-join पंक्ति उत्पन्न करता है, डायमेंशन-की विशिष्टता पर निर्भर करती है, इसलिए उस बाधा की अलग से निगरानी करें।

प्रदर्शन के लिए, जॉइन स्टेज में p50, p95, और अधिकतम टास्क अवधि, अधिकतम-से-मीडियन शफल रीड, स्पिल, GC, OOM, टास्क रीट्राइज़, स्टेज समय और कुल रनटाइम की तुलना करें। लागत के लिए, एक्ज़ीक्यूटर-घंटे, शफल बाइट्स, और आउटपुट-फ़ाइल गणना रिकॉर्ड करें। 45 मिनट के भीतर समाप्त होना केवल एक गेट है। एक रन जो शफल को दोगुना करके, परिणामों को बदलकर, या अगले दिन के नए हॉटस्पॉट पर विफल होकर SLA को पूरा करता है, वह स्वीकार्य नहीं है।

एक ऐतिहासिक तिथि को दोबारा चलाकर (replaying) रिलीज़ करें, फिर एक नई डेटा तिथि की शैडोइंग (shadowing) करें और परिणामों की तुलना करें। हॉट-की सेट, अधिकतम-से-मीडियन टास्क-इनपुट अनुपात और अज्ञात-की शेयर की निगरानी करें। अपस्ट्रीम रिलीज़ के बाद 32% UNKNOWN की छलांग को डेटा-क्वालिटी अलर्ट को भी ट्रिगर करना चाहिए, जिससे सिमेंटिक रीग्रेशन का काम में देरी करने से पहले ही पता चल सके।

उच्च-गुणवत्ता वाला नमूना उत्तर

"मैं पहले SQL UI में एक विशिष्ट जॉइन स्टेज के लिए रीग्रेशन को जिम्मेदार ठहराऊंगा। वर्तमान साक्ष्य दृढ़ता से स्कीव का सुझाव देते हैं: 2,000 टास्क्स में, मीडियन शफल रीड 1.1 GiB है, एक टास्क 720 GiB रीड करता है, और वह टास्क दूसरे एक्ज़ीक्यूटर पर जाने के बाद भी धीमा रहता है। मैं अंतिम अनुकूली योजना, स्पिल और GC का निरीक्षण करूँगा, फिर सटीक उत्पादन नॉर्मलाइज़ेशन के बाद कीज़ की प्रोफ़ाइल करूँगा। मैं डायमेंशन-की विशिष्टता पर भी ज़ोर दूंगा। कई UNKNOWN डायमेंशन पंक्तियों का मतलब होगा कि लक्षण में जॉइन-आउटपुट विस्फोट शामिल है।

एक अद्वितीय डायमेंशन और 32% UNKNOWN फैक्ट पंक्तियों को मानते हुए, एक हैश-शफल बकेट स्ट्रैगलर की व्याख्या करता है। अधिक पार्टीशन्स अतिरिक्त बकेट्स बनाते हैं लेकिन उस की (key) को विभाजित नहीं करते हैं, और अधिक एक्ज़ीक्यूटर मेमोरी केवल OOM को टालती है। मैं पहले यह निर्धारित करूँगा कि क्या अपस्ट्रीम मैपिंग एक रीग्रेशन है। यदि अज्ञात पंक्तियों को किसी डायमेंशन विशेषता की आवश्यकता नहीं है, तो मैं उन्हें जॉइन से अलग कर दूंगा और मूल left-join सिमेंटिक्स के साथ नल विशेषताएँ भरूँगा। यदि उन्हें एक सेंटिनल पंक्ति से मेल खाना चाहिए, तो मैं सत्यापित करूँगा कि AQE skew join वास्तव में अंतिम योजना में दिखाई देता है क्योंकि यह रनटाइम आँकड़ों का उपयोग करके एक स्कीव्ड सॉर्ट-मर्ज-जॉइन पार्टीशन को विभाजित कर सकता है और छोटे पक्ष की प्रतिकृति बना सकता है।

यदि AQE अभी भी रनटाइम को 45 मिनट से ऊपर छोड़ता है, तो मैं लक्षित सॉल्टिंग का उपयोग करूँगा। एक स्थिर event_id प्रत्येक UNKNOWN फैक्ट पंक्ति को नियतात्मक रूप से, उदाहरण के लिए, 32 सॉल्ट्स में से एक को असाइन करेगा। मैं उन 32 सॉल्ट्स में केवल डायमेंशन की UNKNOWN पंक्ति की प्रतिकृति बनाऊँगा; प्रत्येक कोल्ड की सॉल्ट 0 का उपयोग करेगी। प्रत्येक इवेंट अभी भी एक डायमेंशन पंक्ति से मेल खाता है, जबकि कई टास्क्स हॉट-की कार्य साझा करते हैं। मैं बिना माप के 32 को हार्ड-कोड करने के बजाय हॉटस्पॉट बाइट्स और लक्षित टास्क आकार से अंतिम बकेट गणना प्राप्त करूँगा।

सत्यापन के लिए, मैं बेसलाइन के विरुद्ध समान अपरिवर्तनीय इनपुट की तुलना करूँगा। कुल पंक्तियाँ, अद्वितीय इवेंट्स, अज्ञात और बेमेल रिकॉर्ड्स, और व्यावसायिक योग सहमत होने चाहिए। फिर मैं अधिकतम-से-मीडियन शफल रीड, टास्क-अवधि की पूंछ, स्पिल, OOM, स्टेज समय, एक्ज़ीक्यूटर-घंटे और आउटपुट फ़ाइलों की तुलना करूँगा। अंत में, मैं एक ऐतिहासिक तिथि को फिर से चलाऊँगा, एक नई तिथि को शैडो करूँगा, और अज्ञात-की हिस्सेदारी और नए हॉटस्पॉट्स पर सचेत करूँगा। यह साबित करता है कि जॉब 45 मिनट को पूरा करती है, परिणामों को संरक्षित करती है, और जब अपस्ट्रीम वितरण फिर से बदलता है तो अवलोकनीय (observable) बनी रहती है।"

सामान्य गलतियाँ

  • शफल पार्टीशन्स को तुरंत 2,000 से बढ़ाकर 8,000 करना → एक हॉट की अभी भी एक पार्टीशन में हैश होती है जबकि अन्य टास्क छोटे हो जाते हैं → की वितरण को मापें, फिर AQE, सिमेंटिक ब्रांचिंग, या लक्षित सॉल्टिंग के साथ की को विभाजित करें।
  • केवल एक्ज़ीक्यूटर मेमोरी बढ़ाना → यह एक टास्क की सहनशीलता को बढ़ाता है लेकिन 720 GiB के काम और लंबी पूंछ को बरकरार रखता है → पहले अधिकतम पार्टीशन वर्कलोड को कम करें, फिर मापे गए टास्क्स से संसाधनों का आकार तय करें।
  • एक धीमा टास्क देखने के बाद स्कीव घोषित करना → एक खराब नोड, GC, रिमोट फ़ेच, या धीमा UDF भी एक स्ट्रैगलर बना सकता है → टास्क इनपुट, पुनः प्रयास स्थान, स्पिल, GC और निष्पादन योजना की तुलना करें।
  • फैक्ट और डायमेंशन को स्वतंत्र रूप से रैंडमली सॉल्ट करना → सॉल्ट्स मेल खाने में विफल रहते हैं और जॉइन परिणाम खो देते हैं, जबकि पुनः प्रयास गैर-नियतात्मक (nondeterministic) हो सकते हैं → एक स्थिर पंक्ति आईडी से फैक्ट सॉल्ट प्राप्त करें और डायमेंशन पर समान सॉल्ट्स की गणना करें।
  • पूर्ण डायमेंशन को 32 बार दोहराना → एक 180 GB डायमेंशन भारी नेटवर्क और मेमोरी लागत बनाता है → केवल पुष्टि की गई हॉट-की पंक्तियों की प्रतिकृति बनाएं और कोल्ड कीज़ को सॉल्ट 0 पर रखें।
  • 180 GB डायमेंशन को ब्रॉडकास्ट करने के लिए मजबूर करना → प्रत्येक एक्ज़ीक्यूटर को ब्रॉडकास्ट डेटा रखना होगा और OOM के साथ विफल हो सकता है → पहले प्रोजेक्ट करें और मापें; मेमोरी और समवर्ती बजट इसे सुरक्षित साबित करने के बाद ही ब्रॉडकास्ट करें।
  • जॉब को तेज़ बनाने के लिए UNKNOWN को फ़िल्टर करना → आउटपुट सिमेंटिक्स और डाउनस्ट्रीम मेट्रिक्स बदल जाते हैं → अज्ञात-इवेंट अनुबंध स्थापित करें और ब्रांचिंग करते समय भी left-join परिणामों को संरक्षित करें।
  • केवल कुल रनटाइम की तुलना करना → एक स्पष्ट गति छोड़े गए, डुप्लिकेट किए गए, या गलत तरीके से गणना किए गए डेटा से आ सकती है → टास्क वितरण, लागत और SLA की तुलना करने से पहले पंक्ति और व्यावसायिक इनवेरिएंट्स को साबित करें।

अनुवर्ती प्रश्न और उनका उत्तर कैसे दें

AQE सक्षम है। स्कीव्ड जॉइन को विभाजित क्यों नहीं किया गया?

spark.sql.adaptive.enabled, स्कीव-जॉइन स्विच, मीडियन-फ़ैक्टर थ्रेसहोल्ड और एब्सोल्यूट-बाइट थ्रेसहोल्ड के लिए अंतिम अनुकूली योजना और प्रभावी सेटिंग्स का निरीक्षण करें। दोनों थ्रेसहोल्ड का मेल होना चाहिए। पुष्टि करें कि फिजिकल जॉइन एक समर्थित AQE पथ का अनुसरण करता है, रनटाइम आँकड़े उपलब्ध हैं, और एक हिंट या प्लेटफ़ॉर्म ओवरराइड योजना को बाधित नहीं करता है। अतिरिक्त शफल को मापते समय प्रतिनिधि डेटा पर थ्रेसहोल्ड परिवर्तन या बाध्य स्कीव ऑप्टिमाइज़ेशन का परीक्षण करें। यदि योजना को लाभ नहीं मिल सकता है, तो लक्षित सॉल्टिंग का उपयोग करें। कॉन्फ़िगरेशन फ़ाइल में true मान यह साबित नहीं करता है कि निष्पादित योजना ने पार्टीशन को विभाजित किया है।

यदि प्रोजेक्शन डायमेंशन को घटाकर 6 GiB कर देता है, तो क्या आप इसे ब्रॉडकास्ट कर सकते हैं?

6 GiB को अभी भी एक एक्ज़ीक्यूटर-हीप, समवर्ती-टास्क, सीरियलाइज़्ड-आकार, ब्रॉडकास्ट-टाइमआउट और क्लस्टर-स्थिरता मूल्यांकन की आवश्यकता है। ब्रॉडकास्ट बड़े पक्ष के जॉइन-की शफल को हटा सकता है और जिससे हॉटस्पॉट से बचा जा सकता है, लेकिन यह डायमेंशन को एक्ज़ीक्यूटर्स पर भी कॉपी करता है। वास्तविक बाइट्स को साबित करने के लिए आँकड़ों का उपयोग करें, फिर हिंट जोड़ने से पहले उत्पादन-आकार के लोड परीक्षण में पीक मेमोरी और GC का निरीक्षण करें। "फैक्ट टेबल से बहुत छोटा" एक पर्याप्त ब्रॉडकास्ट मानदंड नहीं है।

क्या होगा यदि हॉट कीज़ हर दिन बदलती हैं और HOT_KEYS को मैन्युअल रूप से बनाए नहीं रखा जा सकता है?

AQE की रनटाइम प्रतिक्रिया को प्राथमिकता दें। यदि स्पष्ट सॉल्टिंग अभी भी आवश्यक है, तो रिकॉर्ड, बाइट, या लागत थ्रेसहोल्ड का उपयोग करके मुख्य जॉब से पहले एक सीमित (bounded) हॉट-की टेबल बनाएं, फिर सॉल्टेड पथ चुनने के लिए उस छोटी टेबल को ब्रॉडकास्ट करें। डेटा तिथि के अनुसार सूची का संस्करण बनाएं और इसे एक थ्रेसहोल्ड, गणना सीमा और फ़ॉलबैक दें। यह एक प्लानिंग स्टेज और परिचालन स्थिति जोड़ता है, इसलिए स्थिर SLA लाभों को जटिलता को सही ठहराना चाहिए।

यदि धीमा ऑपरेशन groupBy है, तो क्या आप अभी भी एक डायमेंशन की प्रतिकृति बनाते हैं?

नहीं। मर्ज करने योग्य एग्रीगेशन के लिए, हॉट पंक्तियों को सॉल्ट करें, (key, salt) द्वारा आंशिक एग्रीगेट्स की गणना करें, फिर उन आंशिक परिणामों को key द्वारा मर्ज करें। यह एक हॉट की के इनपुट को टास्क्स में वितरित करता है, जबकि दूसरा स्टेज कम संख्या में आंशिक परिणामों को प्रोसेस करता है। बताएं कि क्या एग्रीगेट सुरक्षित रूप से मर्ज करने योग्य है। विश्व स्तर पर ऑर्डर की गई या गैर-मर्ज करने योग्य स्थिति एक अलग एल्गोरिदम के बिना तकनीक का उपयोग नहीं कर सकती है।

आप 32 सॉल्ट बकेट्स कैसे चुनते हैं?

निचली सीमा (lower bound) के लिए हॉट-पार्टीशन बाइट्स को लक्षित टास्क इनपुट से विभाजित करें, फिर उपलब्ध कोर, छोटे पक्ष की प्रतिकृति, शेड्यूलर ओवरहेड और आउटपुट-फ़ाइल बाधाओं को ध्यान में रखें। यदि 720 GiB प्रति टास्क लगभग 32 GiB तक गिरना चाहिए, तो सैद्धांतिक निचली सीमा लगभग 23 है, इसलिए इस परिदृश्य में 32 एक उचित प्रयोग है। अधिकतम टास्क इनपुट, स्टेज समय और एक्ज़ीक्यूटर-घंटों पर कई उम्मीदवारों की तुलना करें, और हॉटस्पॉट वृद्धि के लिए सीमित हेडरूम रखें।

क्या सट्टा निष्पादन (speculative execution) इस स्ट्रैगलर को हल कर सकता है?

एक नियतात्मक स्कीव्ड टास्क का डुप्लिकेट अभी भी उसी 720 GiB पार्टीशन को रीड करता है, जो आमतौर पर दो एक्ज़ीक्यूटर्स पर महंगे काम को दोहराता है। सट्टा (Speculation) रुक-रुक कर धीमे नोड या क्षणिक जिटर के लिए अधिक उपयोगी है। जाँचें कि क्या वही टास्क कहीं और पुनः प्रयास करने के बाद भी धीमा रहता है। यदि इसका इनपुट आउटलायर बना रहता है, तो डेटा कार्य को डुप्लिकेट करने के बजाय उसे विभाजित करें।

सार्वजनिक स्रोत

संबंधित प्रश्न