सीनमैक (विकिमीडिया कॉमन्स) द्वारा गोल्डफील्ड्स पाइपलाइन
यदि आप अपने PostgreSQL-आधारित एप्लिकेशन के प्रदर्शन को अनुकूलित करने का प्रयास कर रहे हैं तो आप शायद सामान्य टूल पर ध्यान केंद्रित कर रहे हैं:EXPLAIN (BUFFERS, ANALYZE) , pg_stat_statements , auto_explain , log_statement_min_duration , आदि.हो सकता है कि आप लॉक विवाद को log_lock_waits . के साथ देख रहे हों , अपने चेकपॉइंट के प्रदर्शन की निगरानी करना, आदि भी।
लेकिन क्या आपने नेटवर्क विलंबता के बारे में सोचा? ? गेमर नेटवर्क लेटेंसी के बारे में जानते हैं, लेकिन क्या आपको लगता है कि यह आपके एप्लिकेशन सर्वर के लिए मायने रखता है?
विलंबता मायने रखती है
विशिष्ट क्लाइंट/सर्वर राउंड-ट्रिप नेटवर्क विलंबता 0.01ms (लोकलहोस्ट) से स्विच किए गए नेटवर्क के ~0.5ms, वाईफाई के 5ms, ADSL के 20ms, इंटरकांटिनेंटल रूटिंग के 300ms, और उपग्रह और WWAN लिंक जैसी चीजों के लिए और भी अधिक हो सकती है। ।
एक तुच्छ चुनें सर्वर-साइड निष्पादित करने के लिए 0.1ms के क्रम में ले सकता है। एक तुच्छ INSERT 0.5ms ले सकते हैं।
हर बार जब आपका एप्लिकेशन कोई क्वेरी चलाता है तो उसे सर्वर द्वारा सफलता/विफलता और संभवतः परिणाम सेट, क्वेरी मेटाडेटा आदि के जवाब के लिए प्रतीक्षा करनी पड़ती है। इसमें कम से कम एक नेटवर्क राउंड ट्रिप विलंब होता है।
जब आप छोटे, सरल प्रश्नों के साथ काम कर रहे होते हैं तो नेटवर्क विलंबता आपके प्रश्नों के निष्पादन समय के सापेक्ष महत्वपूर्ण हो सकती है यदि आपका डेटाबेस आपके एप्लिकेशन के समान होस्ट पर नहीं है।
कई एप्लिकेशन, विशेष रूप से ORM, में
इसी तरह, यदि आप किसी ORM से डेटाबेस को पॉप्युलेट कर रहे हैं, तो आप शायद सैकड़ों-हजारों तुच्छ INSERT कर रहे हैं s… और सर्वर के ठीक होने की पुष्टि करने के लिए हर एक के बाद प्रतीक्षा कर रहा है।
क्वेरी निष्पादन समय पर ध्यान केंद्रित करने और उसे अनुकूलित करने का प्रयास करना आसान है, लेकिन एक तुच्छ INSERT INTO ...VALUES ... के साथ आप इतना ही कर सकते हैं . कुछ इंडेक्स और बाधाओं को छोड़ दें, सुनिश्चित करें कि यह एक लेन-देन में बैच है, और आपने बहुत कुछ किया है।
सभी नेटवर्क प्रतीक्षा से छुटकारा पाने के बारे में क्या? यहां तक कि एक लैन पर भी वे हजारों प्रश्नों को जोड़ना शुरू कर देते हैं।
कॉपी करें
विलंबता से बचने का एक तरीका COPY . का उपयोग करना है . PostgreSQL की COPY सपोर्ट का उपयोग करने के लिए आपके एप्लिकेशन या ड्राइवर को CSV जैसी पंक्तियों का सेट तैयार करना होगा और उन्हें निरंतर क्रम में सर्वर पर स्ट्रीम करना होगा। या सर्वर से आपके एप्लिकेशन को CSV जैसी स्ट्रीम भेजने के लिए कहा जा सकता है।
किसी भी तरह से, ऐप अन्य प्रश्नों के साथ एक कॉपी को इंटरलीव नहीं कर सकता है, और कॉपी-इन्सर्ट को सीधे एक गंतव्य तालिका में लोड किया जाना चाहिए। एक आम तरीका है कॉपी एक अस्थायी तालिका में, फिर वहां से एक INSERT INTO ... चुनें ... . करें , अद्यतन करें ... से .... , से हटाएं ... उपयोग करके... , आदि एक ही ऑपरेशन में मुख्य तालिकाओं को संशोधित करने के लिए कॉपी किए गए डेटा का उपयोग करने के लिए।
यदि आप सीधे अपना स्वयं का SQL लिख रहे हैं, तो यह आसान है, लेकिन कई एप्लिकेशन फ्रेमवर्क और ORM इसका समर्थन नहीं करते हैं, साथ ही यह केवल सरल INSERT को सीधे ही बदल सकता है। . आपके एप्लिकेशन, फ्रेमवर्क या क्लाइंट ड्राइवर को COPY द्वारा आवश्यक विशेष प्रतिनिधित्व के लिए रूपांतरण से निपटना होगा , किसी भी आवश्यक प्रकार के मेटाडेटा को स्वयं देखें, आदि।
(उल्लेखनीय ड्राइवर जो करते हैं समर्थन कॉपी libpq, PgJDBC, psycopg2, और Pg रत्न शामिल हैं... लेकिन जरूरी नहीं कि उनके ऊपर बने फ्रेमवर्क और ORM हों।)
PgJDBC - बैच मोड
PostgreSQL के JDBC ड्राइवर के पास इस समस्या का समाधान है। यह 8.4 से PostgreSQL सर्वर में मौजूद समर्थन पर और JDBC API की बैचिंग सुविधाओं पर बैच भेजने के लिए निर्भर करता है सर्वर से पूछे जाने वाले प्रश्नों की पुष्टि के लिए केवल एक बार प्रतीक्षा करें कि पूरा बैच ठीक चला।
खैर, सिद्धांत रूप में। वास्तव में कुछ कार्यान्वयन चुनौतियां इसे सीमित करती हैं ताकि बैचों को केवल कुछ सौ प्रश्नों के सर्वोत्तम रूप में ही किया जा सके। ड्राइवर केवल उन प्रश्नों को चला सकता है जो परिणाम पंक्तियों को बैच किए गए हिस्सों में लौटाते हैं यदि यह पता लगा सकता है कि परिणाम समय से पहले कितने बड़े होंगे। उन सीमाओं के बावजूद, Statement.executeBatch() . का उपयोग करें बल्क डेटा लोड करने वाले दूरस्थ डेटाबेस इंस्टेंस जैसे कार्य करने वाले अनुप्रयोगों को एक बड़ा प्रदर्शन बढ़ावा दे सकता है।
क्योंकि यह एक मानक एपीआई है, इसका उपयोग उन अनुप्रयोगों द्वारा किया जा सकता है जो कई डेटाबेस इंजनों में काम करते हैं। उदाहरण के लिए, हाइबरनेट JDBC बैचिंग का उपयोग कर सकता है, हालांकि यह डिफ़ॉल्ट रूप से ऐसा नहीं करता है।
libpq और बैचिंग
अधिकांश (सभी?) अन्य PostgreSQL ड्राइवरों के पास बैचिंग के लिए कोई समर्थन नहीं है। PgJDBC PostgreSQL प्रोटोकॉल को पूरी तरह से स्वतंत्र रूप से लागू करता है, जबकि अधिकांश अन्य ड्राइवर आंतरिक रूप से C लाइब्रेरी libpq का उपयोग करते हैं। जिसे PostgreSQL के हिस्से के रूप में आपूर्ति की जाती है।
libpq बैचिंग का समर्थन नहीं करता है। इसमें एक एसिंक्रोनस नॉन-ब्लॉकिंग एपीआई है, लेकिन क्लाइंट के पास अभी भी एक समय में "फ्लाइट में" केवल एक ही क्वेरी हो सकती है। किसी अन्य को भेजने से पहले उस क्वेरी के परिणाम प्राप्त होने तक उसे प्रतीक्षा करनी चाहिए।
PostgreSQL सर्वर ठीक बैचिंग का समर्थन करता है, और PgJDBC पहले से ही इसका उपयोग करता है। इसलिए मैंने libpq . के लिए बैच समर्थन लिखा है और इसे अगले PostgreSQL संस्करण के लिए एक उम्मीदवार के रूप में प्रस्तुत किया। चूंकि यह केवल क्लाइंट को बदलता है, अगर स्वीकार किया जाता है तो पुराने सर्वर से कनेक्ट होने पर यह अभी भी चीजों को गति देगा।
मुझे libpq . के लेखकों और उन्नत उपयोगकर्ताओं की प्रतिक्रिया में वास्तव में दिलचस्पी होगी -आधारित क्लाइंट ड्राइवर और libpq . के डेवलपर -आधारित अनुप्रयोग। यदि आप इसे आज़माना चाहते हैं तो यह पैच PostgreSQL 9.6beta1 के शीर्ष पर ठीक लागू होता है। दस्तावेज़ीकरण विस्तृत है और एक व्यापक उदाहरण कार्यक्रम है।
प्रदर्शन
मैंने सोचा था कि एक होस्टेड डेटाबेस सेवा जैसे आरडीएस या हेरोकू पोस्टग्रेस एक अच्छा उदाहरण होगा जहां इस तरह की कार्यक्षमता उपयोगी होगी। विशेष रूप से, उन्हें हमारे अपने नेटवर्क से एक्सेस करना वास्तव में दिखाता है कि विलंबता कितनी चोट पहुंचा सकती है।
~320ms नेटवर्क विलंबता पर:
- बिना बैचिंग के 500 इंसर्ट:<जोर>167.0sजोर>
- बैचिंग के साथ 500 इंसर्ट:<जोर>1.2sजोर>
... जो 120x से अधिक तेज है।
आप आमतौर पर ऐप सर्वर और डेटाबेस के बीच एक अंतरमहाद्वीपीय लिंक पर अपना ऐप नहीं चला रहे होंगे, लेकिन यह विलंबता के प्रभाव को उजागर करने का काम करता है। यहां तक कि एक यूनिक्स सॉकेट से लेकर लोकलहोस्ट तक मैंने 10000 इंसर्ट के लिए 50% से अधिक प्रदर्शन सुधार देखा।
मौजूदा ऐप्स में बैच करना
दुर्भाग्य से मौजूदा अनुप्रयोगों के लिए बैचिंग को स्वचालित रूप से सक्षम करना संभव नहीं है। ऐप्स को थोड़ा अलग इंटरफ़ेस का उपयोग करना पड़ता है जहां वे प्रश्नों की एक श्रृंखला भेजते हैं और उसके बाद ही परिणाम मांगते हैं।
एसिंक्रोनस libpq इंटरफ़ेस का उपयोग करने वाले ऐप्स को अनुकूलित करना काफी सरल होना चाहिए, खासकर यदि वे गैर-अवरुद्ध मोड और select() का उपयोग करते हैं। /मतदान () /एपोल () /WaitForMultipleObjectsEx कुंडली। सिंक्रोनस libpq . का उपयोग करने वाले ऐप्स इंटरफेस में और बदलाव की जरूरत होगी।
अन्य क्लाइंट ड्राइवरों में बैचिंग
इसी तरह, क्लाइंट ड्राइवरों, फ्रेमवर्क और ओआरएम को आमतौर पर बैचिंग के उपयोग की अनुमति देने के लिए इंटरफ़ेस और आंतरिक परिवर्तनों की आवश्यकता होगी। यदि वे पहले से ही एक ईवेंट लूप और गैर-अवरुद्ध I/O का उपयोग कर रहे हैं, तो उन्हें संशोधित करना काफी आसान होना चाहिए।
मैं पाइथन, रूबी, आदि उपयोगकर्ताओं को इस कार्यक्षमता तक पहुंचने में सक्षम देखना चाहता हूं, इसलिए मैं यह देखने के लिए उत्सुक हूं कि कौन रुचि रखता है। ऐसा करने में सक्षम होने की कल्पना करें:
आयात करें भविष्य में भविष्य के लिए:परिणाम =भविष्य। परिणाम # प्रतीक्षा करता है यदि परिणाम अभी तक तैयार नहीं है ... परिणाम संसाधित करें ...conn.commit()एसिंक्रोनस बैच निष्पादन क्लाइंट स्तर पर जटिल नहीं होना चाहिए।
कॉपी सबसे तेज है
जहां व्यावहारिक ग्राहकों को अभी भी COPY का पक्ष लेना चाहिए . मेरे लैपटॉप के कुछ परिणाम यहां दिए गए हैं:
बैच्ड, अनबैच्ड और COPYbatch इंसर्ट के साथ 1000000 पंक्तियों को सम्मिलित करना बीता हुआ:23.715315sक्रमिक इंसर्ट बीता हुआ:36.150162sCOPY बीता हुआ:1.743593sहो गया।
काम को बैचने से स्थानीय यूनिक्स सॉकेट कनेक्शन पर भी आश्चर्यजनक रूप से बड़े प्रदर्शन को बढ़ावा मिलता है…। लेकिन कॉपी करें दोनों अलग-अलग इंसर्ट को धूल में बहुत पीछे छोड़ देता है।
कॉपी Use का उपयोग करें ।
छवि
इस पोस्ट के लिए छवि पश्चिमी ऑस्ट्रेलिया में पर्थ के पास मुंडारिंग वियर से अंतर्देशीय (रेगिस्तान) सोने के मैदानों तक गोल्डफील्ड्स जल आपूर्ति योजना पाइपलाइन की है। यह प्रासंगिक है क्योंकि इसे खत्म होने में इतना समय लगा और इतनी तीव्र आलोचना हुई कि इसके डिजाइनर और मुख्य प्रस्तावक, सी. वाई. ओ'कॉनर ने कमीशन में डालने से 12 महीने पहले आत्महत्या कर ली। स्थानीय लोग अक्सर (गलत तरीके से) कहते हैं कि उनकी मृत्यु <जोर>बादजोर> पाइपलाइन तब बनाई गई थी जब कोई पानी नहीं बहता था - क्योंकि इसमें इतना समय लगा कि सभी ने मान लिया कि पाइपलाइन परियोजना विफल हो गई है। फिर हफ्तों बाद, पानी डाला गया।