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

डेटा इंजीनियरिंग इंटरव्यू: आप Apache Iceberg स्कीमा को सुरक्षित रूप से कैसे इवॉल्व करते हैं?

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

प्रश्न

एक Iceberg फ़ैक्ट टेबल कई इंजनों द्वारा लिखी जाती है और इसमें 18 महीने का इतिहास शामिल है। आपको customer_name को first_name और last_name में विभाजित करना होगा और पुरानी क्वेरीज़, समवर्ती राइट्स, स्ट्रीमिंग रीड्स और रोलबैक सुरक्षा को बनाए रखते हुए दैनिक पार्टीशनिंग को मासिक पार्टीशनिंग में बदलना होगा। स्कीमा इवोल्यूशन, पार्टीशन इवोल्यूशन, रोलआउट क्रम, सत्यापन और रिकवरी की व्याख्या करें।

प्रॉम्प्ट और संदर्भ

एक Apache Iceberg फ़ैक्ट टेबल 18 महीनों के ऑर्डर स्टोर करती है जबकि Spark बैच जॉब्स, Flink स्ट्रीम्स और Trino क्वेरीज़ एक साथ चलती हैं। टीम customer_name को first_name और last_name में विभाजित करना चाहती है और पार्टीशनिंग को दिन से महीने में बदलना चाहती है। सभी ऐतिहासिक फ़ाइलों को फिर से नहीं लिखा जा सकता है, पुरानी क्वेरीज़ को काम करते रहना चाहिए, स्ट्रीमिंग राइट्स को रोका नहीं जा सकता है, और एक असफल रोलआउट रिवर्सिबल होना चाहिए।

इंटरव्यू यह परीक्षण करता है कि क्या उम्मीदवार स्कीमा कम्पैटिबिलिटी, फ़ील्ड आइडेंटिटी, पार्टीशन लेआउट, स्नैपशॉट कमिट्स और रीडर/राइटर अपग्रेड को अलग-अलग समझता है। Iceberg स्थिर फ़ील्ड ID और स्वतंत्र स्कीमा व पार्टीशन इवोल्यूशन का दस्तावेजीकरण करता है; एक मजबूत उत्तर केवल "टेबल बदलें और बैकफ़िल करें" कहने के बजाय इन गारंटियों को एक चरणबद्ध माइग्रेशन में बदल देता है।

इंटरव्यूअर क्या मूल्यांकन कर रहा है

नामों और फ़ील्ड आइडेंटिटी के बीच, स्कीमा इवोल्यूशन और ऐतिहासिक बैकफ़िल के बीच, और एक नए पार्टीशन स्पेक और पुरानी फ़ाइलों को भौतिक रूप से स्थानांतरित करने के बीच अंतर की तलाश करें। उम्मीदवार को स्नैपशॉट एटॉमिसीटी, इंजन कम्पैटिबिलिटी और रोलबैक साक्ष्य की व्याख्या करनी चाहिए, जिसमें यह भी शामिल है कि जब कोई राइटर माइग्रेशन के साथ रेस की स्थिति में होता है तो क्या होता है।

स्पष्टीकरण संबंधी प्रश्न

  • कौन से Spark, Flink, Trino, कैटलॉग और Iceberg वर्ज़न टेबल को पढ़ते और लिखते हैं?
  • क्या customer_name को विभिन्न भाषाओं, एकल नामों और गोपनीयता प्रतिबंधों में विश्वसनीय रूप से पार्स किया जा सकता है?
  • क्या पुराने क्लाइंट पोजीशन्स, SELECT * या सीरियलाइज़्ड स्कीमा पर निर्भर हैं?
  • क्या रीडर्स पार्टीशन स्पेक्स को मिला सकते हैं और फिर भी डेट प्रेडिकेट पुशडाउन लागू कर सकते हैं?
  • स्ट्रीमिंग चेकपॉइंट्स और स्कीमा वर्ज़न्स का समन्वय कैसे किया जाता है?
  • क्या कैटलॉग एटॉमिक कमिट्स, स्नैपशॉट रिटेंशन और ID द्वारा रोलबैक का समर्थन करता है?

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

“मैं इंजन कम्पैटिबिलिटी और कॉलम उपयोग की सूची बनाऊंगा, Iceberg फ़ील्ड ID का उपयोग करके नए कॉलम जोड़ूंगा, और अवलोकन अवधि के दौरान पुराने कॉलम को बनाए रखूंगा। पार्टीशन इवोल्यूशन अलग है: नई फ़ाइलें मासिक ट्रांसफ़ॉर्म का उपयोग करती हैं जबकि पुरानी फ़ाइलें पढ़ने योग्य बनी रहती हैं। मैं पहले कम्पैटिबल रीडर्स, फिर राइटर्स, फिर कंज्यूमर्स को रोल आउट करूंगा, क्वेरीज़, चेकपॉइंट्स और समवर्ती कमिट्स को सत्यापित करूंगा, और रोलबैक के लिए पिछले स्नैपशॉट को बनाए रखूंगा। ऐतिहासिक बैकफ़िल एक वर्ज़न जॉब है, न कि कोई अंतर्निहित स्कीमा परिवर्तन।”

चरण-दर-चरण समाधान

चरण 1: एक कम्पैटिबिलिटी मैट्रिक्स और परिवर्तन अनुबंध परिभाषित करें

स्कीमा, फ़ील्ड ID, वर्तमान पार्टीशन स्पेक्स, स्नैपशॉट रिटेंशन, राइटर वर्ज़न्स और रीडर वर्ज़न्स को रिकॉर्ड करें। कॉलम सेमेंटिक्स और पार्टीशन लेआउट के लिए अलग अनुबंध बनाए रखें ताकि पार्सिंग, रीनेमिंग और भौतिक रीराइट्स एक अपारदर्शी कमिट न बन जाएं।

विभाजन को पहले एडिटिव के रूप में लें: first_name और last_name जोड़ें, customer_name को बनाए रखें, और व्हाइटस्पेस, मोनोन्यूम्स, बहुभाषी नामों और पार्स विफलताओं के लिए नियम परिभाषित करें। पुराने कॉलम को चुपचाप कोई नया अर्थ न दें। सभी कंज्यूमर्स के माइग्रेट होने के बाद ही इसे हटाने की योजना बनाएं।

चरण 2: कॉलम पोजीशन्स के बजाय फ़ील्ड ID का उपयोग करें

Iceberg फ़ील्ड्स को स्थिर ID द्वारा बाँधता है। एक परिवर्तन रिकॉर्ड इस प्रकार दिख सकता है:

text
old: id=7 customer_name:string
new: id=21 first_name:string, id=22 last_name:string
old id=7 remains until consumers migrate

डिलीशन के बाद किसी भिन्न अर्थ के लिए कभी भी ID 7 का पुन: उपयोग न करें। नेस्टेड struct, map और list चाइल्ड ID की भी जांच करें। CI को पहले/बाद के ID मैप की तुलना करनी चाहिए; केवल नाम बदलना सुरक्षित इवोल्यूशन का प्रमाण नहीं है।

चरण 3: बैकफ़िल को ऑनलाइन राइट्स से अलग करें

बैच और स्ट्रीमिंग राइटर्स को पहले नए कॉलम भरने दें और पार्स-गुणवत्ता मेट्रिक्स प्रकाशित करें। ऐतिहासिक फ़ाइलों को बाद में प्रति पार्टीशन बैकफ़िल करें। एक निश्चित स्नैपशॉट या वॉटरमार्क पढ़ें, और इनपुट स्नैपशॉट, कोड वर्ज़न, रो काउंट्स, पार्स विफलताओं और चेकसम को एक मेनिफेस्ट में रिकॉर्ड करें। यदि कोई समवर्ती Iceberg कमिट विरोध करता है, तो नवीनतम स्नैपशॉट को फिर से पढ़ें और पुनः प्रयास करें; कभी भी किसी अन्य राइटर के कमिट को ओवरराइट न करें।

यदि किसी ऐतिहासिक नाम को विश्वसनीय रूप से पार्स नहीं किया जा सकता है, तो काल्पनिक पहचान डेटा बनाने के बजाय null के साथ name_parse_status रखें। बैकफ़िल एक अलग वर्ज़न गणना है, न कि स्कीमा कमांड की ही कोई आवश्यकता।

चरण 4: पार्टीशन स्पेक को स्वतंत्र रूप से इवॉल्व करें

नए डेटा के लिए महीने के ट्रांसफ़ॉर्म का उपयोग करके एक पार्टीशन स्पेक जोड़ें, जबकि पुरानी फ़ाइलें दिन के स्पेक को बनाए रखती हैं। प्लानर को प्रत्येक फ़ाइल के स्पेक की पहचान करनी चाहिए और उसे सही ढंग से प्रून करना चाहिए।

text
spec-0: day(ts)      -> existing files
spec-1: month(ts)    -> new files

एक शैडो टेबल में स्कैन वॉल्यूम, प्रूनिंग और छोटी फ़ाइलों के व्यवहार का परीक्षण करें। कंज्यूमर्स को डायरेक्टरी नामों से ऑब्जेक्ट-स्टोरेज पाथ बनाने के बजाय कैटलॉग के माध्यम से मेटाडेटा पढ़ना चाहिए।

चरण 5: रीडर और राइटर रिलीज़ को चरणबद्ध करें

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

प्रत्येक परिवर्तन को एक छोटा स्नैपशॉट बनाएं और राइटर, कैटलॉग, ID मैप और पार्टीशन स्पेक रिकॉर्ड करें। एक ही अवलोकन विंडो में कॉलम विलोपन, प्रकार परिवर्तन और बड़े रीराइट को संयोजित करने से बचें।

चरण 6: कमिट्स को गेट करें और रोलबैक सुरक्षित रखें

ID, प्रकारों और स्पेक्स पर स्ट्रक्चरल जांच; रो काउंट्स, नल दरों, पार्स विफलताओं, एग्रीगेट्स और दिनांक स्लाइस पर डेटा जांच; और पुराने/नए SQL, चेकपॉइंट रिकवरी, कमिट संघर्षों और प्रूनिंग पर व्यवहार संबंधी जांच चलाएं। समीक्षा के लिए अनपार्स किए गए नमूनों को सुरक्षित रखें।

रिलीज़ से पहले previous_snapshot_id और new_snapshot_id को सहेजें। यदि कोई गेट विफल हो जाता है, तो कैटलॉग को वापस पुराने स्नैपशॉट पर इंगित करें, नई फ़ाइलों और साक्ष्यों को बनाए रखें, नए कॉलम की आवश्यकता वाले कंज्यूमर्स को रोकें, और निश्चित इनपुट से प्रभावित पार्टीशन्स को फिर से चलाएं।

मॉडल उत्तर

“मैं पहले फ़ील्ड ID के लिए Spark, Flink, Trino और कैटलॉग समर्थन की सूची बनाऊंगा, फिर customer_name को बनाए रखते हुए दो नए कॉलम जोड़ूंगा। एक डिटरमिनिस्टिक पार्सर और name_parse_status गुणवत्ता को मापते हैं; एक ऐतिहासिक बैकफ़िल एक निश्चित स्नैपशॉट, कोड वर्ज़न और एक मेनिफेस्ट का उपयोग करता है। नई फ़ाइलें month(ts) का उपयोग करती हैं जबकि पुरानी फ़ाइलें day(ts) का उपयोग करती हैं; टेबल मेटाडेटा दोनों को संभालता है।”

“मैं कम्पैटिबल रीडर्स, फिर राइटर्स, फिर कंज्यूमर्स को अपग्रेड करूंगा। रिलीज़ से पहले मैं ID, नल्स, एग्रीगेट्स, पुरानी और नई क्वेरीज़, स्ट्रीम रिकवरी और समवर्ती कमिट्स को मान्य करूंगा। मैं पिछले स्नैपशॉट को बनाए रखूंगा और यदि कोई हार्ड गेट विफल हो जाता है तो कैटलॉग पॉइंटर को वापस रोल कर दूंगा।”

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

  • कॉलम की स्थिति को पहचान के रूप में उपयोग करना → पुराने बाइट्स गलत पढ़े जाते हैं → स्थिर फ़ील्ड ID मान्य करें।
  • रीनेम को एक नए सिमेंटिक कॉलम के रूप में मानना → पुराने मान एक नया अर्थ प्राप्त करते हैं → जोड़ें, बहिष्कृत (deprecate) करें, फिर हटाएं।
  • स्पेक बदलने के बाद प्रत्येक पुरानी फ़ाइल को स्थानांतरित करना → भारी कमिट्स और कमजोर रोलबैक → स्पेक्ट्स को सह-अस्तित्व में रहने दें और चुनिंदा रूप से रीराइट करें।
  • पहले केवल नए-कॉलम वाले रीडर को अपग्रेड करना → पुराने राइटर्स नल उत्सर्जित करते हैं → कम्पैटिबल रीडर्स, राइटर्स, फिर कंज्यूमर्स।
  • केवल स्कीमा कमांड का परीक्षण करना → क्वेरीज़ या स्ट्रीम रिकवरी अभी भी विफल होती हैं → संरचनात्मक, डेटा और व्यवहार गेट्स चलाएं।
  • सीधे लाइव स्नैपशॉट में बैकफ़िल करना → कोई स्पष्ट रिकवरी पॉइंट नहीं होता है → वर्ज़न किए गए आउटपुट और स्नैपशॉट ID का उपयोग करें।

फ़ॉलो-अप और उत्तर

फ़ॉलो-अप 1: हटाए गए फ़ील्ड ID का पुन: उपयोग क्यों नहीं किया जाना चाहिए?

पुरानी डेटा फ़ाइलों में अभी भी मूल ID शामिल होती है। इसका पुन: उपयोग करने से रीडर्स पुराने बाइट्स को एक नए सिमेंटिक फ़ील्ड के रूप में व्याख्यायित करते हैं, इसलिए एक नया ID आवंटित करें और जब तक कंज्यूमर्स और ऐतिहासिक रीप्ले माइग्रेट न हो जाएं तब तक पुराने फ़ील्ड को बनाए रखें।

फ़ॉलो-अप 2: क्या पुराने और नए पार्टीशन स्पेक्स सुरक्षित रूप से सह-अस्तित्व में रह सकते हैं?

हाँ, यदि मेटाडेटा प्रत्येक फ़ाइल के स्पेक को रिकॉर्ड करता है और वास्तविक इंजन ट्रांसफ़ॉर्म्स और प्रेडिकेट्स को सही ढंग से लागू करते हैं। डायरेक्टरी नामों से व्यवहार का अनुमान लगाने के बजाय उत्पादन जैसी क्वेरीज़ के साथ प्रूनिंग, समय-क्षेत्र व्यवहार और छोटी-फ़ाइल गणनाओं को सत्यापित करें।

फ़ॉलो-अप 3: आप समवर्ती कमिट संघर्ष से कैसे उबरते हैं?

नवीनतम स्नैपशॉट पढ़ें, पुष्टि करें कि इनपुट मान्य रहता है, केवल प्रभावित पार्टीशन्स की पुनर्गणना करें, और फिर से सबमिट करें। किसी अन्य राइटर के स्नैपशॉट पर कभी भी पुरानी मेटाडेटा फ़ाइल को ज़बरदस्ती न डालें; बार-बार होने वाले संघर्षों के लिए कम समवर्तीता या छोटे स्लाइस की आवश्यकता होती है।

फ़ॉलो-अप 4: पुराने कॉलम को कब हटाया जा सकता है?

रीडर्स, राइटर्स, रीप्ले, ऑडिट और एक्सपोर्ट के माइग्रेट होने के बाद, अवलोकन अवधि स्थिर होती है, और बनाए रखे गए स्नैपशॉट रोलबैक विंडो को कवर करते हैं। डिलीट सबमिट करने से पहले छिपे हुए कंज्यूमर्स की सूची बनाएं।

फ़ॉलो-अप 5: जो नाम पार्स नहीं किए जा सकते उनका क्या होना चाहिए?

null के साथ एक कारण और मूल मान का एक नियंत्रित संदर्भ लिखें, भाषा और प्रारूप के अनुसार निगरानी करें, और पहचान फ़ील्ड में कोई अनुमान लगाने से बचें। इसे बाद के संस्करण या मानव समीक्षा वर्कफ़्लो में ठीक करें।

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

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