जब आप एक युवा परियोजना पर काम कर रहे होते हैं तो आप लगातार ऐसे निर्णय ले रहे होते हैं जो बाद में स्केल करना आसान या कठिन बना देंगे। कभी-कभी थोड़े तकनीकी ऋण को अर्जित करके तेजी से शिप करने के लिए अल्पकालिक लाभ चुनना अच्छा होता है। लेकिन दूसरी बार हम तकनीकी ऋण उठाते हैं क्योंकि हमें नहीं पता था कि कोई विकल्प था।
मैं ऐसा कह सकता हूं क्योंकि यहां Honeybadger पर, हमने कुछ चीजें कीं जो हमारे लिए जीवन को जितना होना चाहिए था उससे कहीं ज्यादा कठिन बना दिया। अगर हमने कुछ प्रमुख बिंदुओं को समझा होता तो स्केलिंग बहुत कम दर्दनाक होती।
UUID का उपयोग करें
जब आप "प्राइमरी की" कहते हैं तो हम में से अधिकांश एक ऑटो-इन्क्रीमेंटिंग नंबर के बारे में सोचते हैं। यह छोटे सिस्टम के लिए अच्छा काम करता है, लेकिन जैसे-जैसे आप स्केल करते हैं यह एक बड़ी समस्या पेश करता है।
किसी भी क्षण केवल एक डेटाबेस सर्वर प्राइमरी की उत्पन्न कर सकता है। इसका मतलब है सभी राइट को एक ही सर्वर से गुजरना होगा। यदि आप प्रति सेकंड हजारों राइट करना चाहते हैं तो यह बुरी खबर है।
प्राइमरी की के रूप में UUID का उपयोग करना इस समस्या को दरकिनार करता है। यदि आप उनसे परिचित नहीं हैं, तो UUID विशिष्ट पहचानकर्ता हैं जो इस तरह दिखते हैं: 123e4567-e89b-12d3-a456-426655440000।
विकिपीडिया उनका वर्णन इस प्रकार करता है:
जब मानक विधियों के अनुसार उत्पन्न किए जाते हैं, तो UUID व्यावहारिक उद्देश्यों के लिए अद्वितीय होते हैं, बिना केंद्रीय पंजीकरण प्राधिकरण या उन्हें उत्पन्न करने वाले पक्षों के बीच समन्वय की आवश्यकता के बिना। संभावना है कि एक UUID डुप्लिकेट होगा शून्य नहीं है, लेकिन शून्य के इतने करीब है कि इसे नगण्य माना जा सकता है।
इस प्रकार, कोई भी एक UUID बना सकता है और किसी चीज़ की पहचान करने के लिए इसका उपयोग लगभग निश्चितता के साथ कर सकता है कि पहचानकर्ता किसी ऐसी चीज़ को पहचानने के लिए पहले से बनाए गए को डुप्लिकेट नहीं करता है, और भविष्य में डुप्लिकेट नहीं होगा। स्वतंत्र पक्षों द्वारा UUID के साथ लेबल की गई जानकारी को इसलिए बाद में एक ही डेटाबेस में जोड़ा जा सकता है, या उसी चैनल पर प्रेषित किया जा सकता है, बिना पहचानकर्ताओं के बीच संघर्ष को हल करने की आवश्यकता के बिना।
जब आप प्राइमरी की के रूप में UUID का उपयोग करते हैं, तो सभी राइट को अब एक ही डेटाबेस से नहीं गुजरना पड़ता। इसके बजाय आप उन्हें कई सर्वरों पर फैला सकते हैं।
इसके अलावा वे आपको डेटाबेस में सेव होने से पहले रिकॉर्ड की आईडी उत्पन्न करने जैसे काम करने की लचीलापन देते हैं। यह उपयोगी हो सकता है यदि आप रिकॉर्ड को कैश या सर्च सर्वर को भेजना चाहते हैं, लेकिन डेटाबेस लेनदेन के पूरा होने का इंतजार नहीं करना चाहते।
Rails ऐप्स में डिफ़ॉल्ट रूप से UUID सक्षम करना आसान है। बस कॉन्फ़िगरेशन फ़ाइल संपादित करें:
# config/application.rb config.active_record.primary_key = :uuid
आप Rails माइग्रेशन के साथ व्यक्तिगत तालिका के लिए UUID का उपयोग कर सकते हैं:
create_table :users, id: :uuid do |t| t.string :name end
यह एक सरल कॉन्फ़िगरेशन विकल्प है जो, यदि आप विकास शुरू करते समय इसे सक्षम करते हैं, तो जब आप स्केल करने का प्रयास करते हैं तो आपको बहुत परेशानी से बचाएगा। एक अतिरिक्त बोनस के रूप में, यह बॉट्स और बुरे अभिनेताओं के लिए आपके निजी URL का अनुमान लगाना कठिन बना देगा।
काउंट्स और काउंटर्स
एक बार जब आप देखना शुरू करते हैं, तो काउंट्स और काउंटर्स हर जगह होते हैं। आपका ईमेल क्लाइंट अपठित ईमेल की संख्या प्रदर्शित करता है। ब्लॉग में एक पेजिनेशन फूटर होता है जो पेज की संख्या की गणना करने के लिए पोस्ट की कुल संख्या का उपयोग करता है।
काउंटर्स के साथ दो स्केलिंग समस्याएं हैं:
select count(*) from usersजैसे डेटाबेस क्वेरी स्वाभाविक रूप से धीमे हैं। वे सचमुच अपने परिणाम उत्पन्न करने के लिए रिकॉर्डसेट में प्रत्येक रिकॉर्ड के माध्यम से लूप करते हैं। यदि आपके पास एक मिलियन रिकॉर्ड हैं, तो इसमें कुछ समय लगेगा।- काउंटर कैश का उपयोग करके काउंटर्स को गति देने के प्रयास काम करते हैं, लेकिन वे कई डेटाबेस सर्वरों पर राइट फैलाने की आपकी क्षमता को सीमित करते हैं।
सबसे आसान समाधान जहां भी संभव हो काउंटर्स का उपयोग करने से बचना है। यह प्रारंभिक डिजाइन करते समय बहुत आसान है।
उदाहरण के लिए, आप काउंट के बजाय डेट-रेंज द्वारा पेज करना चुन सकते हैं। आप एक हल्के उपयोगी आंकड़े को नहीं दिखाना चुन सकते हैं जिसे बाद में उत्पन्न करना नरक होगा। आपको विचार मिल गया।
समाप्त और वेयरहाउसिंग डेटा
आपके पास मौजूद RAM, CPU और डिस्क IO को देखते हुए एक सिंगल Postgres तालिका में आप कितना डेटा स्टोर कर सकते हैं, इसकी एक ऊपरी सीमा है। इसका मतलब है कि एक बिंदु आएगा जब आपको अपने मुख्य तालिकाओं से पुराना डेटा निकालना होगा।
आइए एक सरल मामला देखें। आप एक डेटाबेस में एक साल से अधिक पुराने रिकॉर्ड हटाना चाहते हैं जो कुछ गीगाबाइट बड़ा है।
यदि आपने पहले कभी इस मुद्दे से निपटा नहीं है तो आप कुछ ऐसा करने के लिए ललचा सकते हैं:
MyRecords.where("created_at < ?", 1.year.ago).destroy
समस्या यह है कि यह क्वेरी चलने में दिन या सप्ताह लगेंगे। आपका डेटाबेस बस बहुत बड़ा है।
यह एक विशेष रूप से दर्दनाक समस्या है, क्योंकि अक्सर आपको इसका एहसास तब तक नहीं होता जब तक बहुत देर हो चुकी होती है। जब उनकी कंपनी युवा होती है और उनके डेटाबेस में 1,000 रिकॉर्ड होते हैं तो शायद ही कोई डेटा पर्जिंग रणनीतियों के बारे में सोचता है।
यदि आप आगे की योजना बनाने का प्रबंधन करते हैं, तो एक आसान समाधान है। बस अपनी तालिकाओं को पार्टीशन करें। अपने सभी डेटा को my_records में लिखने के बजाय आप इस सप्ताह का डेटा my_records_1 में लिखते हैं और अगले सप्ताह का डेटा my_records_2 में। जब पिछले सप्ताह को हटाने का समय आता है, तो बस drop table my_records_1 करें। डिलीट के विपरीत, यह क्वेरी बहुत जल्दी पूरी होती है।
तारीख के अलावा अन्य फ़ील्ड द्वारा पार्टीशन करना भी संभव है। जो भी आपके उपयोग-मामले के लिए समझ में आता है।
pg_partman नामक एक Postgres एक्सटेंशन भी है जो सभी विवरणों का ख्याल रखता है और आपको अपने कोड की एक भी लाइन बदले बिना अपने डेटाबेस को पार्टीशन करने की अनुमति देता है। या, यदि आप रूबी में पार्टीशनिंग को प्रबंधित करना पसंद करते हैं, तो partitionable नामक एक आसान जेम है
विदाई के शब्द
अगली बार जब आप खुद को स्क्रैच से एक प्रोजेक्ट बनाते हुए पाएं, तो मैं आपको स्केलिंग के बारे में सोचने के लिए कुछ पल लेने के लिए प्रोत्साहित करूंगा। इस पर जुनून न रखें। इस पर दिन खर्च न करें कि HAML या ERB का उपयोग करना है या नहीं। लेकिन अपने आप से पूछें कि क्या कोई आसान जीत नहीं है जिसे आप केवल आगे की योजना बनाकर हासिल कर सकते हैं।