KoderKoder.ai
प्राइसिंगएंटरप्राइज़शिक्षानिवेशकों के लिए
लॉग इनशुरू करें

उत्पाद

प्राइसिंगएंटरप्राइज़निवेशकों के लिए

संसाधन

हमसे संपर्क करेंसपोर्टशिक्षाब्लॉग

कानूनी

प्राइवेसी पॉलिसीउपयोग की शर्तेंसुरक्षास्वीकार्य उपयोग नीतिदुरुपयोग रिपोर्ट करें

सोशल

LinkedInTwitter
Koder.ai
भाषा

© 2026 Koder.ai. सर्वाधिकार सुरक्षित।

होम›ब्लॉग›Go + Postgres प्रदर्शन ट्यूनिंग: एक केंद्रित API प्लेबुक
14 दिस॰ 2025·8 मिनट

Go + Postgres प्रदर्शन ट्यूनिंग: एक केंद्रित API प्लेबुक

AI-जनरेटेड APIs के लिए Go + Postgres प्रदर्शन ट्यूनिंग प्लेबुक: कनेक्शन पूल, EXPLAIN देखें, स्मार्ट इंडेक्स, सुरक्षित पेजिनेशन, और तेज़ JSON आकार दें।

Go + Postgres प्रदर्शन ट्यूनिंग: एक केंद्रित API प्लेबुक

Go APIs पर Postgres के लिए "धीमा" कैसा दिखता है

AI-जेनरेट किए गए APIs शुरुआती परीक्षण में तेज़ लग सकते हैं। आप किसी एंडपॉइंट को कुछ बार हिट करते हैं, डाटासेट छोटा होता है, और रिक्वेस्ट एक-एक कर आते हैं। फिर असली ट्रैफिक आता है: मिश्रित एंडपॉइंट्स, बर्स्टी लोड, ठंडे कैश, और अपेक्षित से ज़्यादा पंक्तियाँ। वही कोड अचानक रैंडमली धीमा महसूस होने लगता है, हालांकि असल में कुछ टूटा नहीं होता।

धीमा आम तौर पर कुछ तरीकों से दिखता है: लेटेंसी स्पाइक्स (ज़्यादातर रिक्वेस्ट ठीक रहते हैं, कुछ 5x से 50x धीमे हो जाते हैं), टाइमआउट (एक छोटा प्रतिशत फेल होता है), या CPU गरम चलना (Postgres क्वेरी वर्क से CPU, या Go CPU JSON, goroutines, लॉगिंग, और retries से)।

एक सामान्य परिदृश्य एक लिस्ट एंडपॉइंट है जिसमें लचीला सर्च फ़िल्टर है और जो बड़ी JSON रिस्पॉन्स लौटाता है। टेस्ट DB में वह कुछ हजार पंक्तियों को स्कैन करके जल्दी खत्म कर देता है। प्रोडक्शन में वह कुछ मिलियन पंक्तियों को स्कैन करता है, उन्हें सॉर्ट करता है, और तभी LIMIT लागू होता है। API अभी भी "काम" करता है, पर p95 लेटेंसी विस्फोट कर जाती है और बर्स्ट के दौरान कुछ रिक्वेस्ट टाइमआउट हो जाते हैं।

डेटाबेस की धीमी गति को ऐप की धीमी गति से अलग करने के लिए मानसिक मॉडल सरल रखें।

अगर डेटाबेस धीमा है, तो आपका Go हैंडलर अपने समय का बड़ा हिस्सा क्वेरी के इंतज़ार में बिताता है। आप कई रिक्वेस्ट्स को "in flight" फंसा हुआ देख सकते हैं जबकि Go CPU सामान्य दिखता है।

अगर ऐप धीमा है, तो क्वेरी जल्दी खत्म होती है, लेकिन क्वेरी के बाद समय चला जाता है: बड़े रिस्पॉन्स ऑब्जेक्ट बनाना, JSON मैर्शल करना, प्रति-रो अतिरिक्त क्वेरियाँ चलाना, या हर रिक्वेस्ट पर ज्यादा काम करना। Go CPU बढ़ता है, मेमोरी बढ़ती है, और लेटेंसी रिस्पॉन्स साइज के साथ बढ़ती है।

लॉन्च से पहले "पर्याप्त अच्छा" प्रदर्शन पूर्णता नहीं है। कई CRUD एंडपॉइंट्स के लिए लक्ष्य स्थिर p95 लेटेंसी (केवल औसत नहीं), बर्स्ट के दौरान अनुमानित व्यवहार, और अपेक्षित पीक पर टाइमआउट न होना होना चाहिए। लक्ष्य साफ़ है: जब डेटा और ट्रैफिक बढ़े तो कोई अनपेक्षित धीमी रिक्वेस्ट न हो, और जब कुछ ड्रीफ्ट करे तो स्पष्ट संकेत हों।

बेसलाइन पहले: वे कुछ संख्याएँ जो मायने रखती हैं

कुछ भी ट्यून करने से पहले तय करें कि API के लिए "अच्छा" क्या है। बिना बेसलाइन के, सेटिंग्स बदलने में घंटे निकल सकते हैं और फिर भी आपको पता नहीं चलता कि आप ने सुधार किया या सिर्फ बॉटलनेक को आगे बढ़ाया।

तीन नंबर अक्सर कहानी का अधिकांश हिस्सा बताते हैं:

  • p95 रिक्वेस्ट लेटेंसी (औसत नहीं)
  • एरर रेट (HTTP 5xx, टाइमआउट, कैंसलेड रिक्वेस्ट)
  • प्रति रिक्वेस्ट DB समय (हर रिक्वेस्ट Postgres पर कितना इंतज़ार करता है)

p95 वह "खराब दिन" मीट्रिक है। अगर p95 ऊँचा है पर औसत ठीक है, तो कुछ रिक्वेस्ट्स बहुत ज़्यादा काम कर रही हैं, लॉक पर ब्लॉक हो रही हैं, या स्लो प्लान ट्रिगर कर रही हैं।

धीमी क्वेरियों को जल्दी दिखाएँ। Postgres में प्रीलॉन्च टेस्टिंग के लिए स्लो क्वेरी लॉगिंग को कम थ्रेशोल्ड पर चालू करें (उदा. 100–200 ms) और पूरा स्टेटमेंट लॉग करें ताकि आप उसे SQL क्लाइंट में कॉपी कर सकें। यह अस्थायी रखें—प्रोडक्शन में हर स्लो क्वेरी लॉग करना जल्दी शोर पैदा कर देता है।

अगला, असली दिखने वाली रिक्वेस्ट्स के साथ टेस्ट करें, सिर्फ एक "hello world" रूट नहीं। एक छोटा सेट भी पर्याप्त है अगर वह वही करे जो यूज़र्स करेंगे: फ़िल्टर और सॉर्टिंग वाला लिस्ट कॉल, कुछ जोइन वाले डिटेल पेज, वेलिडेशन के साथ क्रिएट/अपडेट, और पार्शल मैच के साथ सर्च-स्टाइल क्वेरी।

अगर आप एक स्पेक से एंडपॉइंट्स जेनरेट कर रहे हैं (उदा. Koder.ai जैसी टूल से), तो वही छोटे सेट नियमित रूप से चलाएँ। इससे इंडेक्स, पेजिनेशन ट्वीक, और क्वेरी रीट्राइट्स को नापना आसान होगा।

अंत में, एक लक्ष्य चुने जिसे आप जोर से कह सकें। उदाहरण: "ज़्यादातर रिक्वेस्ट्स 50 concurrent users पर p95 में 200 ms से कम रहते हैं, और एरर 0.5% से कम रहते हैं।" सटीक संख्याएँ आपके प्रोडक्ट पर निर्भर होंगी, पर एक स्पष्ट लक्ष्य अनंत टिंकरिंग रोकता है।

कनेक्शन पूलिंग जो Postgres को स्थिर रखे

एक कनेक्शन पूल सीमित संख्या में खुली DB कनेक्शन्स रखता है और उन्हें रीयूज़ करता है। बिना पूल के, हर रिक्वेस्ट नई कनेक्शन खोल सकती है, और Postgres सत्रों का प्रबंधन करने में समय और मेमोरी खर्च करता है बजाए क्वेरियों को रन करने के।

लक्ष्य यह है कि Postgres उपयोगी काम कर रहा हो, न कि बहुत सी कनेक्शन्स के बीच context-switching कर रहा हो। यह अक्सर पहली सार्थक जीत होती है, खासकर AI-जेनरेट API जिनमें धीरे-धीरे चैटी एंडपॉइंट्स बन जाते हैं।

सरल शुरुआती सेटिंग्स

Go में आप सामान्यतः max open connections, max idle connections, और connection lifetime ट्यून करते हैं। कई छोटे APIs के लिए सुरक्षित शुरुआती बिंदु अक्सर आपके CPU को एक छोटा गुणक होता है (अक्सर कुल 5 से 20 कनेक्शन्स), समान संख्या idle रखें, और कनेक्शन्स को समय-समय पर रीसायकल करें (उदा. हर 30–60 मिनट)।

अगर आप कई API इंस्टेंस चला रहे हैं, याद रखें कि पूल गुणा हो जाता है। 10 इंस्टेंस पर 20 कनेक्शन्स का पूल Postgres पर 200 कनेक्शन्स भेजता है — और इसी तरह टीमें अनपेक्षित रूप से कनेक्शन लिमिट में फंस जाती हैं।

कैसे पता करें कि पूल समस्या है

पूल समस्याएँ धीमी SQL से अलग महसूस होती हैं।

अगर पूल बहुत छोटा है, तो रिक्वेस्ट्स Postgres तक पहुँचने से पहले ही इंतज़ार करती हैं। लेटेंसी स्पाइक होती है, पर DB CPU और क्वेरी टाइम सामान्य दिख सकते हैं।

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

जल्दी से अलग करने का तरीका: अपने DB कॉल्स को दो हिस्सों में टाइम करें: कनेक्शन के लिए बिताया समय बनाम क्वेरी निष्पादन का समय। अगर ज्यादातर समय "waiting" में है तो पूल बॉटलनेक है। अगर ज्यादातर समय "in query" में है, तो SQL और इंडेक्स पर ध्यान दें।

उपयोगी त्वरित जाँचें:

  • पूल स्टैट्स (open, in-use, idle) लॉग करें और देखें कि क्या in-use max पर अटकी रहती है।
  • स्टेजिंग में कनेक्शन हासिल करने पर timeout जोड़ें ताकि इंतज़ार तेज़ी से फेल हो।
  • Postgres में सक्रिय कनेक्शन्स मॉनिटर करें और max_connections के कितने करीब हैं।
  • पुष्टि करें कि हर रिक्वेस्ट rows बंद करता है और कनेक्शन्स तुरंत रिलीज़ करता है।
  • उसी संख्या के ऐप इंस्टेंस के साथ लोड टेस्ट चलाएँ जो आप चलाने की योजना बना रहे हैं।

pgxpool बनाम database/sql

अगर आप pgxpool का उपयोग करते हैं, तो आपको Postgres-फर्स्ट पूल मिलता है जिसमें स्पष्ट स्टैट्स और Postgres व्यवहार के लिए अच्छे डिफ़ॉल्ट होते हैं। अगर आप database/sql इस्तेमाल करते हैं, तो आपको एक सामान्य इंटरफ़ेस मिलता है जो कई DB के साथ काम करता है, पर आपको पूल सेटिंग्स और ड्राइवर व्यवहार के बारे में स्पष्ट होना पड़ता है।

एक व्यावहारिक नियम: अगर आप पूरी तरह Postgres पर हैं और डायरेक्ट कंट्रोल चाहते हैं, तो pgxpool अक्सर सरल होता है। अगर आप लाइब्रेरीज़ पर निर्भर हैं जो database/sql की उम्मीद करती हैं, तो वहीं रहें, पूल स्पष्ट रूप से सेट करें, और इंतज़ारों को मापें।

उदाहरण: एक एंडपॉइंट जो 20 ms चल सकता है, पर 100 concurrent users पर वह 2 s पर छलांग लगा देता है। अगर लॉग्स में दिखता है कि 1.9 s कनेक्शन के लिए इंतज़ार में गया, तो तब तक क्वेरी ट्यूनिंग मदद नहीं करेगी जब तक पूल और कुल Postgres कनेक्शन्स सही आकार के न हों।

क्वेरी प्लानिंग: EXPLAIN आउटपुट की तेज़ पढ़ाई

जब कोई एंडपॉइंट धीमा लगता है, तो देखें कि Postgres असल में क्या कर रहा है। EXPLAIN की एक त्वरित पढ़ाई अक्सर मिनटों में फिक्स का संकेत देती है।

API जो सही SQL भेजता है उसी पर यह चलाएँ:

EXPLAIN (ANALYZE, BUFFERS)
SELECT id, status, created_at
FROM orders
WHERE user_id = $1 AND status = $2
ORDER BY created_at DESC
LIMIT 50;

कुछ लाइनों का सबसे अधिक महत्व होता है। ऊपर वाला नोड देखें (Postgres ने जो चुना) और नीचे के टोटल्स (कितना समय लगा)। फिर estimated बनाम actual rows की तुलना करें। बड़े गैप अक्सर प्लान गलत होने का संकेत हैं।

प्रमुख लाइनों का सामान्य अर्थ

अगर आप Index Scan या Index Only Scan देखें, तो Postgres इंडेक्स का उपयोग कर रहा है, जो आमतौर पर ठीक है। Bitmap Heap Scan मध्यम आकार के मैचों के लिए ठीक हो सकता है। Seq Scan मतलब टेबल की पूरी पढ़ाई हुई—यह केवल तब ठीक है जब टेबल छोटी हो या लगभग हर रो मैच करे।

सामान्य रेड फ़्लैग्स:

  • बड़ी टेबल पर Seq Scan
  • अनुमानित बनाम वास्तविक rows में बड़ा अंतर (उदा. 10 अनुमानित vs 10,000 वास्तविक)
  • Sort जो अधिकांश समय ले रहा हो (ORDER BY के साथ अक्सर जुड़ा)
  • स्कैन के बाद "Filter:" द्वारा बहुत सारी पंक्तियाँ हटना
  • BUFFERS में उच्च shared read blocks (बहुत डाटा पढ़ा गया)

प्लान क्यों गलत होते हैं (और आसान फिक्स)

धीमे प्लान अक्सर कुछ ही पैटर्न से आते हैं:

  • आपके WHERE + ORDER BY पैटर्न के लिए मिसिंग इंडेक्स (उदा. (user_id, status, created_at))
  • मिसमैच्ड टाइप्स (उदा. UUID कॉलम की तुलना टेक्स्ट पैरामीटर से), जो इंडेक्स उपयोग को रोक सकते हैं
  • WHERE में फंक्शन का उपयोग (उदा. WHERE lower(email) = $1), जो स्कैन मजबूर कर सकता है जब तक आप मिलते हुए एक्सप्रेशन इंडेक्स ना जोड़ें

अगर प्लान अजीब दिखे और अनुमान बहुत गलत हों, तो अक्सर स्टैट्स पुरानी होती हैं। ANALYZE चलाएँ (या autovacuum को पकड़ने दें) ताकि Postgres वर्तमान रो काउंट और वैल्यू डिस्ट्रीब्यूशन सीख ले। यह बड़े इम्पोर्ट के बाद या जब नए एंडपॉइंट्स तेज़ी से बहुत डाटा लिखना शुरू करें तो मायने रखता है।

उन क्वेरीज़ के लिए इंडेक्सिंग जो आप सचमुच चलाते हैं

स्रोत को अपने पास रखें
पूरा सोर्स एक्सपोर्ट करें ताकि आप pprof चला सकें, मेट्रिक्स जोड़ सकें और EXPLAIN आउटपुट लोकली वेलिडेट कर सकें।
कोड एक्सपोर्ट करें

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

एक उपयोगी तरीका यह सोचना है: इंडेक्स किसी खास सवाल के लिए शॉर्टकट है। अगर आपका API अलग सवाल पूछता है, तो Postgres शॉर्टकट को अनदेखा कर देता है।

फ़िल्टर्स + सॉर्ट ऑर्डर के चारों ओर इंडेक्स बनाएं

अगर कोई एंडपॉइंट account_id से फ़िल्टर करता है और created_at DESC से सॉर्ट करता है, तो एक संयुक्त कॉम्पोजिट इंडेक्स अक्सर दो अलग-अलग इंडेक्स से बेहतर होता है। इससे Postgres सही रो ढूंढकर उन्हें सही क्रम में कम काम में लौटाने में मदद मिलती है।

अमूमन नियम जो सही रहते हैं:

  • जिन कॉलम्स पर आप सबसे ज़्यादा फ़िल्टर करते हैं उन्हें इंडेक्स करें, फिर उस कॉलम को जोड़ें जिससे आप सॉर्ट करते हैं।
  • कॉम्पोजिट इंडेक्स को छोटा रखें। दो कॉलम सामान्य हैं; तीन कभी-कभी ठीक है; इससे ज़्यादा आमतौर पर घबराहट की निशानी है।
  • सबसे सेलेक्टिव फिल्टर पहले रखें (जो परिणाम सबसे ज़्यादा घटाते हैं)।
  • अलग-अलग इंडेक्स से बचें जो एक बेहतर कॉम्पोजिट इंडेक्स द्वारा पूरी तरह कवर किए जा चुके हों।
  • कई "शायद उपयोगी" इंडेक्स की बजाय एक अच्छी चुनी हुई इंडेक्स पसंद करें।

उदाहरण: अगर आपके API में GET /orders?status=paid है और यह हमेशा नवीनतम पहले दिखाता है, तो (status, created_at DESC) जैसा इंडेक्स अच्छा फीट है। अगर अधिकांश क्वेरियाँ ग्राहक द्वारा भी फ़िल्टर करती हैं, तो (customer_id, status, created_at) बेहतर हो सकता है, पर केवल तभी जब वह वही पैटर्न प्रोडक्शन में चलता हो।

सामान्य फ़िल्टर्स के लिए partial इंडेक्स

अगर अधिकांश ट्रैफ़िक एक संकुचित स्लाइस पर पड़ता है, तो partial index सस्ता और तेज़ हो सकता है। उदाहरण के लिए, अगर आपका ऐप ज़्यादातर सक्रिय रिकॉर्ड पढ़ता है, तो केवल WHERE active = true पर इंडेक्स करना इंडेक्स को छोटा रखता है और उसे मेमोरी में बने रहने की संभावना बढ़ाती है।

इंडेक्स मदद करता है या नहीं यह पुष्टि करने के लिए त्वरित जाँचें:

  • EXPLAIN (या सुरक्षित माहौल में EXPLAIN ANALYZE) चलाएँ और देखें कि क्या इंडेक्स स्कैन आपकी क्वेरी से मेल खाती है।
  • इंडेक्स के साथ और बिना टाइमिंग और पढ़ी गई रोज़ की तुलना करें।
  • "Rows Removed by Filter" उच्च रहे तो अक्सर इंडेक्स आपके फ़िल्टर से मेल नहीं खाता।

अनउपयोगी इंडेक्स को सावधानी से हटाएँ। उपयोग आँकड़े (क्या किसी इंडेक्स को स्कैन किया गया) देखें। एक-एक करके लो-रीस्क विंडो में ड्रॉप करें और रोलबैक प्लान रखें। अनउपयोगी इंडेक्स हानिरहित नहीं हैं — वे हर राइट पर धीमा करते हैं।

ऐसी पेजिनेशन पैटर्न जो समय के साथ धीमी न हों

पेजिनेशन अक्सर वह जगह होती है जहाँ एक तेज़ API धीमा महसूस होने लगता है, भले ही DB स्वस्थ हो। पेजिनेशन को UI डिटेल न मानकर क्वेरी डिज़ाइन समस्या समझें।

क्यों LIMIT/OFFSET धीमा होता है

LIMIT/OFFSET सरल दिखता है, पर गहरे पेज अक्सर महंगे होते हैं। Postgres अभी भी उन रोज़ के पास से चलता है जिन्हें आप स्किप कर रहे हैं (और अक्सर उन्हें सॉर्ट भी करता है)। पेज 1 कुछ दर्जन रोज़ छू सकता है। पेज 500 में डेटाबेस कई हजारों रोज़ को स्कैन और डिसकार्ड कर सकता है सिर्फ 20 रिज़ल्ट्स लौटाने के लिए।

यह तब भी अस्थिर परिणाम दे सकता है जब रिक्वेस्ट्स के बीच पंक्तियाँ डाली या हटाई जाती हैं। उपयोगकर्ता डुप्लिकेट देख सकते हैं या आइटम मिस कर सकते हैं क्योंकि "रो 10,000" का मतलब बदल गया।

कीसेट पेजिनेशन (कर्सर) का "last seen" उदाहरण

Keyset पेजिनेशन एक अलग सवाल पूछता है: "मुझे आखिरी देखी गई पंक्ति के बाद अगली 20 पंक्तियाँ दो।" इससे डेटाबेस एक छोटे, स्थिर स्लाइस पर काम करता है।

एक सरल संस्करण बढ़ते id का उपयोग करता है:

SELECT id, created_at, title
FROM posts
WHERE id > $1
ORDER BY id
LIMIT 20;

आपका API next_cursor लौटाता है जो पेज में आखिरी id के बराबर होता है। अगली रिक्वेस्ट उस वैल्यू को $1 के रूप में भेजती है।

टाइम-आधारित सॉर्टिंग के लिए एक स्थिर ऑर्डर और टाई-ब्रेकर का उपयोग करें। created_at अकेला पर्याप्त नहीं है अगर दो पंक्तियों का टाइमस्टैम्प समान हो। एक संयुक्त कर्सर का प्रयोग करें:

WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 20;

कई नियम डुप्लिकेट और गायब रोज़ रोकते हैं:

  • ORDER BY में हमेशा एक यूनिक टाई-ब्रेकर शामिल करें (आम तौर पर id)।
  • हर रिक्वेस्ट में सॉर्ट ऑर्डर समान रखें।
  • कर्सर क्लाइंट्स के लिए अपारदर्शी रखें (encoded created_at और id साथ में)।
  • अगर यूज़र्स फ़िल्टर कर सकते हैं, तो हर पेज पर वही फ़िल्टर लागू रखें।
  • जहाँ संभव हो बदलने योग्य फ़ील्ड्स (status, score) की बजाय अपरिवर्तनीय सॉर्ट फ़ील्ड्स (created time) पसंद करें।

JSON आकार देना: छोटे पेलोड्स से तेज़ रिस्पॉन्स

DB पूल का सही आकार तय करें
ऐसा कोड जेनरेट करें जिसमें समझदार पूल सीमाएँ और टाइमआउट शामिल हों, फिर आत्मविश्वास से लोड टेस्ट करें।
प्रोजेक्ट शुरू करें

एक चौंकाने वाली सामान्य वजह कि API धीमा लगता है वह डेटाबेस नहीं होता—वह रिस्पॉन्स होता है। बड़ा JSON बनाना, भेजना और क्लाइंट का पार्स करना सब समय लेते हैं। सबसे तेज़ जीत अक्सर कम लौटाने में है।

अपने SELECT से शुरू करें। अगर एंडपॉइंट को सिर्फ id, name, और status चाहिए, तो सिर्फ वे कॉलम माँगें। SELECT * टेबल जैसे-जैसे लंबा टेक्स्ट, JSON ब्लॉब और ऑडिट कॉलम जोड़ती है, धीरे-धीरे भारी होता जाता है।

एक और सामान्य धीमापन N+1 रिस्पॉन्स निर्माण है: आप 50 आइटम की लिस्ट फेच करते हैं, फिर संबंधित डाटा जोड़ने के लिए 50 और क्वेरियाँ चलते हैं। यह टेस्ट पास कर सकता है, पर असली ट्रैफिक में धराशायी हो जाता है। एकल क्वेरी पसंद करें जो आपको चाहिए वह लौटाए (ध्यान से जॉइन), या IDs के आधार पर दूसरी क्वेरी जहां दूसरी बैच करता है।

पेलोड्स छोटे रखने के कुछ तरीके बिना क्लाइंट तोड़े:

  • include= फ्लैग (या fields=) रखें ताकि लिस्ट रिस्पॉन्स हल्का रहे और डिटेल ऑप्शनेल हो।
  • नेस्टेड ऐरेज़ को कैप करें (उदा. केवल नवीनतम 10 इवेंटस) और पूरा हिस्ट्री अलग एंडपॉइंट से दें।
  • अगर क्लाइंट्स को सिर्फ कुछ कुंजियाँ चाहिए तो रॉ आंतरिक JSON कॉलम न लौटाएँ।
  • लंबे लेबल्स के बार-बार रिपीट होने की बजाय शॉर्ट कोड्स का उपयोग करें।

JSON Postgres में बनाऊँ या Go में?

दोनों तेज़ हो सकते हैं। चुनें कि आप किसके लिए ऑप्टिमाइज़ कर रहे हैं।

Postgres JSON फ़ंक्शन्स (jsonb_build_object, json_agg) उपयोगी हैं जब आप कम राउंड ट्रिप्स और एक क्वेरी से प्रेडिक्टेबल शेप चाहते हैं। Go में शेपिंग उपयोगी है जब आपको कंडीशनल लॉजिक चाहिए, structs को री-यूज़ करना है, या SQL को आसान रखना है। अगर आपका JSON-बिल्डिंग SQL पढ़ने में कठिन हो जाए, तो उसे ट्यून करना भी मुश्किल हो जाता है।

एक अच्छा नियम: Postgres को फ़िल्टर, सॉर्ट और एग्रीगेट करने दें। फिर Go अंतिम प्रस्तुति संभाले।

अगर आप तेजी से APIs जेनरेट कर रहे हैं (उदा. Koder.ai के साथ), तो शुरुआती चरण में include फ्लैग जोड़ना उन एंडपॉइंट्स को समय के साथ फूलने से रोकता है। यह आपको एक सुरक्षित तरीका देता है फ़ील्ड जोड़ने का बिना हर रिस्पॉन्स को भारी किए।

पहले उपयोगकर्ताओं से पहले एक चरण-दर-चरण ट्यूनिंग पास

आपको अधिकांश परफ़ॉर्मेंस समस्याओं पकड़ने के लिए बड़ा टेस्ट लैब की ज़रूरत नहीं है। एक छोटा, दोहराने योग्य पास वे समस्याएँ उजागर करता है जो ट्रैफ़िक आने पर आउटेज में बदलती हैं, खासकर जब शुरुआती बिंदु जेनरेटेड कोड हो जिसको आप भेजने वाले हैं।

कुछ भी बदलने से पहले एक छोटा बेसलाइन लिखें:

  • सबसे व्यस्त एंडपॉइंट्स के लिए p95 और p99 लेटेंसी
  • एरर रेट और टाइमआउट
  • डेटाबेस CPU और सक्रिय कनेक्शन्स
  • कुल समय के हिसाब से सबसे धीमी 5 क्वेरियाँ (केवल सबसे खराब नहीं)

ट्यूनिंग पास

छोटा शुरू करें, एक-एक करके बदलें, और हर परिवर्तन के बाद फिर से टेस्ट करें।

  1. 10–15 मिनट का लोड टेस्ट चलाएँ जो असली उपयोग जैसा दिखे। उन्हीं एंडपॉइंट्स को हिट करें जो आपके पहले उपयोगकर्ता हिट करेंगे (लॉगिन, लिस्ट पेज, सर्च, क्रिएट)। फिर रूट्स को p95 लेटेंसी और कुल समय के अनुसार सॉर्ट करें।

  2. SQL ट्यून करने से पहले कनेक्शन प्रेशर जांचें। बहुत बड़ा पूल Postgres को ओवरव्हेल्म कर देता है; बहुत छोटा पूल लंबा इंतज़ार बनाता है। बर्स्ट के दौरान कनेक्शन गिनती और कनेक्शन हासिल करने का बढ़ता इंतज़ार देखें। सबसे पहले पूल और idle लिमिट्स समायोजित करें, फिर वही लोड फिर से चलाएँ।

  3. सबसे धीमी क्वेरियों पर EXPLAIN चलाएँ और सबसे बड़े रेड-फ़्लैग को ठीक करें। सामान्य अपराधी बड़ी टेबल पर फुल स्कैन, बड़े रिज़ल्ट सेट पर सॉर्ट, और जॉइंस जो रो काउंट को बढ़ा देते हैं। सर्वाधिक खराब क्वेरी चुनें और उसे 'बोरिंग' बनाइए।

  4. एक इंडेक्स जोड़ें या समायोजित करें, फिर फिर से टेस्ट करें। इंडेक्स तब मदद करते हैं जब वे आपके WHERE और ORDER BY से मिलते हों। एक बार में पाँच नहीं जोड़ें।

  5. रिस्पॉन्स और पेजिनेशन कसें, फिर फिर से टेस्ट करें। अगर एक एंडपॉइंट बड़े JSON ब्लॉब्स के साथ 50 रो लौटाता है, तो DB, नेटवर्क और क्लाइंट सभी को कीमत चुकानी पड़ती है। केवल UI को चाहिए वही फ़ील्ड लौटाएँ, और पेजिनेशन चुनें जो टेबल बढ़ने पर धीमा न हो।

सरल चेंज-लॉग रखें: क्या बदला, क्यों बदला, और p95 में क्या बदला। अगर कोई बदलाव आपके बेसलाइन में सुधार नहीं करता, तो उसे revert कर दें और आगे बढ़ें।

सामान्य गलतियाँ और जाल जिनसे बचें

डोमेन के साथ डिप्लॉय और उपयोग करें
जब आप साझा करने के लिए तैयार हों तो अपना ऐप होस्ट करें और कस्टम डोमेन कनेक्ट करें।
ऐप प्रकाशित करें

Go APIs पर Postgres में अधिकांश परफ़ॉर्मेंस समस्याएँ आत्म-निर्मित होती हैं। अच्छी बात यह है कि कुछ जाँचें लॉन्च से पहले कई समस्याएँ पकड़ लेती हैं।

एक क्लासिक ट्रैप पूल साइज को "स्पीड नॉब" समझना है। इसे "जितना बड़ा हो उतना अच्छा" सेट करना अक्सर सब कुछ धीमा कर देता है। Postgres सत्रों, मेमोरी और लॉक को जुगाड़ने में अधिक समय लगाता है, और आपका ऐप लहरों में टाइमआउट करने लगता है। एक छोटा, स्थिर पूल और अनुमानित concurrency ज़्यादातर मामलों में बेहतर रहता है।

एक और आम गलती "सब कुछ इंडेक्स कर देना" है। अतिरिक्त इंडेक्स पढ़ने में मदद कर सकते हैं, पर वे राइट्स को धीमा करते हैं और कभी-कभी क्वेरी प्लान्स को चौंकाने वाले तरीकों से बदल देते हैं। ट्यून करने से पहले मापें, और इंडेक्स जोड़ने के बाद प्लान्स फिर से जांचें।

पेजिनेशन डेट-डेब्ट धीरे-धीरे चुपके से घुस आता है। ऑफसेट पेजिनेशन शुरुआती चरण में ठीक दिखती है, फिर p95 समय के साथ बढ़ता रहता है क्योंकि DB को और अधिक रोज़ के पास से गुजरना पड़ता है।

JSON पेलोड साइज भी एक छिपा हुआ कर है। कम्प्रेशन बैंडविड्थ कम कर सकता है, पर यह बड़े ऑब्जेक्ट बनाने, अलोकेट करने और पार्स करने की लागत को हटाता नहीं है। फ़ील्ड्स कम करें, गहरी नेस्टिंग से बचें, और केवल वही लौटाएँ जो स्क्रीन को चाहिए।

अगर आप केवल औसत प्रतिक्रिया समय देखते हैं, तो आप वास्तविक यूज़र की पीड़ा को मिस कर देंगे। p95 (और कभी-कभी p99) वहां दिखाई देता है जहाँ पूल सैचुरेशन, लॉक वेट्स, और स्लो प्लान्स पहले दिखते हैं।

एक त्वरित प्री-लॉन्च सेल्फ-चेक:

  • एक छोटे लोड टेस्ट के दौरान पूल वेट टाइम और Postgres कनेक्शन गिनती देखें।
  • एक ही एंडपॉइंट के लिए औसत बनाम p95 की तुलना करें।
  • सुनिश्चित करें कि पेजिनेशन 10x बड़ी टेबल पर भी degrade नहीं करता।
  • लिस्ट एंडपॉइंट्स के लिए रिस्पॉन्स साइज देखें (बाइट्स मायने रखते हैं)।
  • इंडेक्स जोड़ने या फ़िल्टर बदलने के बाद EXPLAIN फिर से चलाएँ।

लॉन्च से पहले त्वरित चेकलिस्ट और अगले कदम

असली उपयोगकर्ता आने से पहले, यह प्रमाण चाहिए कि आपका API दबाव में अनुमानित रहता है। लक्ष्य परफेक्ट नंबर नहीं है—लक्ष्य उन कुछ समस्याओं को पकड़ना है जो टाइमआउट, स्पाइक्स, या डेटाबेस को नया काम स्वीकार करने से रोक देती हैं।

स्टेजिंग वातावरण में जाँचें जो प्रोडक्शन जैसा लगे (समान DB साइज़ रेंज, वही इंडेक्स, वही पूल सेटिंग्स): प्रमुख एंडपॉइंट पर लोड के दौरान p95 लेटेंसी मापें, कुल समय के अनुसार सबसे धीमी क्वेरियों को कैप्चर करें, सबसे खराब क्वेरी पर EXPLAIN (ANALYZE, BUFFERS) चलाकर पुष्टि करें कि यह अपेक्षित इंडेक्स का उपयोग कर रही है, और अपने सबसे व्यस्त रूट्स पर रिस्पॉन्स साइज की सैनीटी-चेक करें।

फिर एक worst-case रन करें जो दिखाता है कि प्रोडक्ट कैसे टूटता है: एक गहरा पेज रिक्वेस्ट करें, सबसे चौड़ा फ़िल्टर लगाएँ, और ठंडे स्टार्ट के साथ आज़माएँ (API रीस्टार्ट करें और वही रिक्वेस्ट पहले हिट करें)। अगर गहरी पेजिनेशन हर पेज पर धीमी हो रही है, तो लॉन्च से पहले cursor-based पेजिनेशन पर जाएँ।

अपनी डिफ़ॉल्ट्स लिख दें ताकि टीम बाद में सुसंगत निर्णय ले: पूल लिमिट्स और टाइमआउट, पेजिनेशन नियम (मैक्स पेज साइज, क्या ऑफसेट की अनुमति है, कर्सर फ़ॉर्मेट), क्वेरी नियम (सिर्फ ज़रूरी कॉलम चुनें, SELECT * से बचें, महंगे फ़िल्टर्स को कैप करें), और लॉगिंग नियम (स्लो क्वेरी थ्रेशोल्ड, सैंपल्स कितनी देर रखें, एंडपॉइंट लैबलिंग)।

अगर आप Koder.ai के साथ Go + Postgres सर्विसेज बनाते और एक्सपोर्ट करते हैं, तो डिप्लॉयमेंट से पहले एक छोटा प्लानिंग पास फ़िल्टर्स, पेजिनेशन, और रिस्पॉन्स शेप इरादतन रखने में मदद करता है। एक बार आप इंडेक्स और क्वेरी शेप ट्यून करने लगें, स्नैपशॉट्स और रोलबैक एक ही जगह से किसी "फिक्स" को जो एक एंडपॉइंट को सुधारे पर दूसरों को नुकसान पहुँचा सकता है—उसे उलटना आसान बनाते हैं। अगर आप इस वर्कफ़्लो पर एक जगह चाहें जहां इटरेट करना है, तो Koder.ai पर koder.ai इस तरह की सेवाओं को जेनरेट और परिष्कृत करने के चारों ओर बनाया गया है और जब आप तैयार हों तो सोर्स एक्सपोर्ट करने की सुविधा देता है।

अक्सर पूछे जाने वाले प्रश्न

मैं जल्दी कैसे बता सकता हूँ कि मेरा Go API Postgres की वजह से धीमा है या मेरे कोड की वजह से?

प्रथमतः DB vänting time और एप्लिकेशन वर्क टाइम को अलग कर के देखें।

  • अगर डेटाबेस धीमा है, हैंडलर ज़्यादातर क्वेरी का इंतज़ार करता है। Go का CPU सामान्य रहता है जबकि रिक्वेस्ट्स "in flight" जमा हो जाते हैं।
  • अगर ऐप धीमा है, तो क्वेरी जल्दी लौटती है पर उसके बाद ऑब्जेक्ट बनाना, प्रति-रो क्वेरी करना, बड़े JSON को मैर्शल करना या लॉगिंग में समय लगता है। Go का CPU और मेमोरी अक्सर रिस्पॉन्स साइज के साथ बढ़ते हैं।

साधारण टाइमिंग जोड़ें: “कनेक्शन का इंतज़ार” और “क्वेरी निष्पादन”—देखें कौन सा पहलू ज्यादा समय ले रहा है।

किस मेट्रिक्स को पहले ट्रैक करना चाहिए इससे पहले कि मैं कुछ भी ट्यून करूँ?

शुरू करने के लिए एक छोटा और दोहराने योग्य बेसलाइन रखें:

  • प्रमुख एंडपॉइंट पर p95 लेटेंसी (औसत नहीं)
  • एरर रेट (5xx, टाइमआउट, कैंसलेशंस)
  • DB टाइम प्रति रिक्वेस्ट (Postgres पर इंतज़ार की गई कुल अवधि)

एक स्पष्ट लक्ष्य चुनें, जैसे “50 concurrent users पर p95 200 ms से कम, एरर < 0.5%।” फिर एक बार में सिर्फ एक चीज़ बदलें और वही रिक्वेस्ट मिक्स फिर से टेस्ट करें।

क्या मुझे Postgres slow query logging चालू कर देना चाहिए, और व्यावहारिक थ्रेशोल्ड क्या है?

हाँ — प्रीलॉन्च टेस्टिंग के दौरान धीमी क्वेरी लॉगिंग चालू कर दें (उदा. 100–200 ms)। पूरा स्टेटमेंट लॉग करें ताकि आप उसे SQL क्लाइंट में कॉपी कर सकें।

ध्यान रखें:

  • प्रोडक्शन में यह जल्दी शोर बन जाता है।
  • बहुत ज़्यादा लॉगिंग ओवरहेड बढ़ा सकती है।

सबसे खराब क्वेरियों को मिलने के बाद आप सैंपलिंग पर स्विच कर सकते हैं या थ्रेशोल्ड बढ़ा सकते हैं।

Go API के लिए Postgres पर आरंभिक कनेक्शन पूल सेटिंग्स क्या अच्छी होती हैं?

एक व्यावहारिक डिफ़ॉल्ट है प्रति API इंस्टेंस CPU को एक छोटा गुणक मानना—अक्सर 5–20 max open connections, समान max idle connections के साथ, और कनेक्शनों को हर 30–60 मिनट पर रीसायकल करना।

दो सामान्य विफलता मोड:

  • पूल बहुत छोटा: रिक्वेस्ट्स कनेक्शन पाने के लिए इंतज़ार करती हैं भले ही Postgres क्वेरी समय ठीक हो।
  • पूल बहुत बड़ा: Postgres कई सक्रिय सत्रों से ओवरलोड हो सकता है और लेटेंसी असमान हो जाती है।

याद रखें कि पूल इंस्टेंसों के साथ गुणा होता है (20 × 10 = 200 कनेक्शन)।

मैं कैसे सुनिश्चित करूँ कि कनेक्शन पूल ही बॉटलनेक है (SQL नहीं)?

DB कॉल को दो हिस्सों में टाइम करें:

  • कनेक्शन का इंतज़ार (pool wait)
  • क्वेरी निष्पादन (Postgres का काम)

अगर अधिकांश समय pool wait में जाता है, तो पूल साइजिंग, टाइमआउट और इंस्टेंस काउंट एडजस्ट करें। अगर अधिकांश समय क्वेरी में है, तो EXPLAIN और इंडेक्स पर ध्यान दें।

सुनिश्चित करें कि आप हमेशा rows बंद कर रहे हैं ताकि कनेक्शन्स जल्दी से पूल में लौटें।

जब कोई एंडपॉइंट धीमा हो तो EXPLAIN में सबसे पहले क्या देखना चाहिए?

EXPLAIN (ANALYZE, BUFFERS) उस सही SQL पर चलाएँ जो आपका API भेजता है और इन चीज़ों पर ध्यान दें:

  • बड़ी टेबल पर Seq Scan
  • अनुमानित बनाम वास्तविक rows में बड़ा अंतर
  • ORDER BY के साथ जुड़े हुए Sort जो समय ले रहे हों
  • "Rows Removed by Filter" बहुत ज़्यादा हो
लिस्ट एंडपॉइंट के लिए सही इंडेक्स कैसे चुनूँ जहाँ फिल्टर और सॉर्ट दोनों हों?

इंडेक्स वही मदद करते हैं जो आपके API की क्वेरी करती है: filters + sort order।

अच्छा उपाय:

  • सामान्य WHERE + ORDER BY पैटर्न के लिए composite index बनाएं।
  • इसे छोटा रखें (अक्सर 2 कॉलम, कभी-कभी 3)।
  • सबसे अधिक सेलेक्टिव फिल्टर पहले रखें, फिर सॉर्ट कॉलम।
Postgres में partial index कब बनाना चाहिए?

जब अधिक ट्रैफ़िक एक अनुमानित उपसमूह पर आता है तो partial index उपयोगी है।

उदाहरण:

  • कई रीड्स केवल active = true के लिए
  • inactive क्वेरियाँ कम

ऐसा partial index (... WHERE active = true) छोटे रहता है, मेमोरी में रहने की संभावना बढ़ती है, और सब कुछ इंडेक्स करने की तुलना में राइट-ओवरहेड कम होता है।

पक्का करने के लिए चलाएँ कि Postgres आपके हाई-ट्रैफ़िक क्वेरीज़ में इसे उपयोग कर रहा है।

LIMIT/OFFSET समय के साथ क्यों धीमा होता है, और इसके बजाय क्या इस्तेमाल करूँ?

LIMIT/OFFSET गहरे पेज पर धीमा हो जाता है क्योंकि Postgres अभी भी उन रोज़ को स्कैन/पार कर रहा होता है जिन्हें आप छोड़ रहे हैं। पेज 1 में कुछ दर्जन रोज़ देखने पड़ सकती हैं, पेज 500 पर हजारों स्कैन और डिसकार्ड करने पड़ सकते हैं।

इसके बजाय keyset (cursor) pagination उपयोग करें:

मेरी DB क्वेरियाँ तेज़ हैं, पर रिस्पॉन्स अभी भी धीमा है—क्या मुझे JSON पेलोड छोटा करना चाहिए?

आम तौर पर हाँ — खासकर लिस्ट एंडपॉइंट्स के लिए। सबसे तेज़ उत्तर वही है जो आप नहीं भेजते।

व्यावहारिक कदम:

  • सिर्फ ज़रूरी कॉलम ही सेलेक्ट करें (SELECT * से बचें)।
  • include= या fields= दें ताकि क्लाइंट भारी फ़ील्ड्स को ऑप्ट-इन करे।
विषय-सूची
Go APIs पर Postgres के लिए "धीमा" कैसा दिखता हैबेसलाइन पहले: वे कुछ संख्याएँ जो मायने रखती हैंकनेक्शन पूलिंग जो Postgres को स्थिर रखेक्वेरी प्लानिंग: EXPLAIN आउटपुट की तेज़ पढ़ाईउन क्वेरीज़ के लिए इंडेक्सिंग जो आप सचमुच चलाते हैंऐसी पेजिनेशन पैटर्न जो समय के साथ धीमी न होंJSON आकार देना: छोटे पेलोड्स से तेज़ रिस्पॉन्सपहले उपयोगकर्ताओं से पहले एक चरण-दर-चरण ट्यूनिंग पाससामान्य गलतियाँ और जाल जिनसे बचेंलॉन्च से पहले त्वरित चेकलिस्ट और अगले कदमअक्सर पूछे जाने वाले प्रश्न
शेयर करें
Koder.ai
Koder के साथ अपना खुद का ऐप बनाएं आज ही!

Koder की शक्ति को समझने का सबसे अच्छा तरीका खुद देखना है।

मुफ्त शुरू करेंडेमो बुक करें
  • BUFFERS में बहुत सारे shared read blocks
  • सबसे बड़ा रेड फ़्लैग पहले ठीक करें; सब कुछ एक साथ ट्यून न करें।

    उदाहरण: यदि आप user_id से फ़िल्टर करते हैं और सबसे नए दिखाते हैं, तो (user_id, created_at DESC) जैसा इंडेक्स अक्सर p95 में बड़ा फर्क लाता है।

    EXPLAIN
  • एक स्थिर सॉर्ट और यूनिक टाई-ब्रेकर (आम तौर पर id) रखें।
  • हर रिक्वेस्ट पर ORDER BY एक जैसा रखें।
  • (created_at, id) जैसे मानों को कर्सर में एन्कोड करें।
  • इससे टेबल बढ़ने पर भी हर पेज की लागत लगभग स्थिर रहती है।

  • नेस्टेड ऐरेज़ को कैप करें (उदा. नवीनतम 10 आइटम) और पूरा हिस्ट्री के लिए अलग एपीआई दें।
  • N+1 पैटर्न से बचें — जो 50 आइटम के लिए 50 और क्वेरियाँ चलाता है। जो चाहिए उसे एक साथ जॉइन या बैच करें।
  • अक्सर केवल पेलोड छोटा करके आप Go CPU, मेमोरी दबाव और टेल लेटेंसी घटा देंगे।