AD

Slow Database Queries को कैसे करें फिक्स: SQL Indexing और EXPLAIN ANALYZE की प्रैक्टिकल केस स्टडी

Slow Database Queries को कैसे करें फिक्स: SQL Indexing और EXPLAIN ANALYZE की प्रैक्टिकल केस स्टडी

जब कोई वेब ऐप्लिकेशन या मोबाइल ऐप धीमा होने लगता है, तो अक्सर डेवलपर्स सर्वर की रैम या सीपीयू बढ़ाने की सोचने लगते हैं। लेकिन 90% मामलों में असली समस्या सर्वर हार्डवेयर में नहीं, बल्कि डेटाबेस में चल रही अन-ऑप्टिमाइज़्ड (Unoptimized) SQL क्वेरीज़ में होती है।

इस केस स्टडी में हम एक काल्पनिक लेकिन बेहद वास्तविक ई-कॉमर्स प्लेटफॉर्म 'दुकानप्लस' के उदाहरण से समझेंगे कि कैसे एक साधारण डेटाबेस इंडेक्स और क्वेरी ट्यूनिंग से 8 सेकंड में लोड होने वाले सर्च पेज को मात्र 45 मिलीसेकंड (ms) में बदला गया।

केस स्टडी परिदृश्य: 'दुकानप्लस' का डेटाबेस बॉटलनेक

दुकानप्लस के पास PostgreSQL डेटाबेस पर चलने वाला एक स्टोर है जिसमें 20 लाख (2 Million) प्रोडक्ट्स और 50 लाख (5 Million) ऑर्डर्स का डेटा है। फेस्टिव सेल के दौरान जैसे ही ट्रैफिक बढ़ा, यूजर्स को 'Product Search' और 'Order History' पेज पर 6 से 10 सेकंड का लोडिंग टाइम दिखने लगा। कई बार डेटाबेस सीपीयू 100% पर पहुंच जाता था और कनेक्शन टाइमआउट होने लगते थे।

समस्या पैदा करने वाली मुख्य SQL क्वेरी

डेवलपर टीम ने पाया कि यूजर जब किसी कैटेगरी में निश्चित प्राइस रेंज के प्रोडक्ट्स सर्च करता है, तो बैकएंड यह क्वेरी चलाता था:

SELECT product_id, title, price, rating 
FROM products 
WHERE category_id = 45 AND price BETWEEN 500 AND 2000 
ORDER BY rating DESC 
LIMIT 20;

चरण 1: EXPLAIN ANALYZE से समस्या की पहचान करना

किसी भी SQL क्वेरी को ऑप्टिमाइज़ करने का पहला नियम है - अंदाज़ा न लगाएं, डेटाबेस का एक्ज़ीक्यूशन प्लान (Execution Plan) देखें। इसके लिए PostgreSQL में EXPLAIN ANALYZE कमांड का उपयोग किया गया।

आउटपुट में क्या मिला?

  • Seq Scan (Sequential Scan): डेटाबेस पूरे 20 लाख रिकॉर्ड्स को एक-एक करके स्कैन (Full Table Scan) कर रहा था क्योंकि category_id और price पर कोई इंडेक्स नहीं बना था।
  • Execution Time: लगभग 7,850 ms (7.85 सेकंड)।
  • Memory Buffers: डिस्क I/O बहुत ज्यादा था, जिससे सर्वर रैम पर भारी दबाव पड़ रहा था।

चरण 2: सही इंडेक्सिंग रणनीति चुनना (Indexing Strategy)

कई डेवलपर्स गलती यह करते हैं कि वे टेबल के हर कॉलम पर अलग-अलग सिंगल इंडेक्स बना देते हैं। इससे डेटाबेस का साइज बढ़ता है और INSERT/UPDATE धीमा हो जाता है। हमें यहां स्मार्ट इंडेक्सिंग की जरूरत थी।

सिंगल इंडेक्स बनाम कम्पोजिट इंडेक्स (Composite Index)

हमारी क्वेरी में तीन चीजें हैं: फिल्टर (category_id, price) और सॉर्टिंग (rating)। इसलिए हमने एक Composite B-Tree Index बनाने का निर्णय लिया।

CREATE INDEX idx_products_cat_price_rating 
ON products (category_id, price, rating DESC);

इस इंडेक्स का क्रम (Order of Columns) बहुत महत्वपूर्ण है:

  1. Equality Column सबसे पहले: category_id पर सटीक मैच (=) है, इसलिए इसे सबसे आगे रखा।
  2. Range Column बीच में: price पर रेंज (BETWEEN) है, इसलिए इसे दूसरे स्थान पर रखा।
  3. Sort Column अंत में: rating DESC से डेटाबेस को अलग से मेमोरी में सॉर्टिंग नहीं करनी पड़ेगी।

चरण 3: इंडेक्स के बाद परफॉर्मेंस की दोबारा जांच

इंडेक्स बनाने के बाद जब दोबारा EXPLAIN ANALYZE चलाया गया, तो रिजल्ट चौंकाने वाले थे:

  • Index Scan: डेटाबेस ने 20 लाख रिकॉर्ड्स के बजाय सिर्फ 1,400 रिकॉर्ड्स का इंडेक्स ट्री स्कैन किया।
  • Execution Time: 7,850 ms से घटकर सिर्फ 38 ms रह गया।
  • CPU Usage: 100% से गिरकर 12% पर आ गया।

चरण 4: क्वेरी राइटिंग की 3 आम गलतियों को ठीक करना

केस स्टडी के दौरान टीम ने बैकएंड कोड में कुछ अन्य गलतियां भी पकड़ीं जो अच्छे इंडेक्स को भी बेकार कर देती हैं:

1. फंक्शन का गलत इस्तेमाल (Functions on Indexed Columns)

कोड में कई जगह यूजरनेम सर्च के लिए ऐसा लिखा गया था: WHERE LOWER(email) = 'user@example.com'। ऐसा करने से नॉर्मल इंडेक्स काम नहीं करता। इसे ठीक करने के लिए या तो डेटा को हमेशा लोअरकेस में स्टोर करें या फिर Functional Index बनाएं:

CREATE INDEX idx_users_lower_email ON users (LOWER(email));

2. वाइल्डकार्ड सर्च (Wildcard Search Trap)

LIKE '%smartphone' जैसी क्वेरी में इंडेक्स काम नहीं करता क्योंकि वाइल्डकार्ड (%) शुरुआत में है। अगर टेक्स्ट सर्च की जरूरत हो, तो PostgreSQL का GIN Index (Trigram) या Full-Text Search का इस्तेमाल करना चाहिए।

3. गैर-ज़रूरी SELECT * से बचना

जब आप SELECT * लिखते हैं, तो डेटाबेस को इंडेक्स के अलावा मुख्य डिस्क ब्लॉक (Heap) से भी डेटा फेच करना पड़ता है। केवल वही कॉलम्स सिलेक्ट करें जिनकी फ्रंटएंड को जरूरत है।

केस स्टडी का अंतिम परिणाम (Before vs After)

पैरामीटर ऑप्टिमाइजेशन से पहले ऑप्टिमाइजेशन के बाद
Query Execution Time 7.85 सेकंड 38 मिलीसेकंड (~99% तेज)
डेटाबेस स्कैन का प्रकार Sequential Scan (2M rows) Index Scan (1,400 rows)
पीक आवर्स में CPU लोड 95% - 100% 10% - 15%

डेटाबेस ऑप्टिमाइजेशन की क्विक चेकलिस्ट

  • नियमित रूप से Slow Query Logs को मॉनिटर करें।
  • हर फॉरेन की (Foreign Key) पर इंडेक्स जरूर बनाएं।
  • जरूरत से ज्यादा इंडेक्स न बनाएं, क्योंकि यह INSERT और UPDATE की स्पीड घटाते हैं।
  • बड़ी टेबल्स के लिए समय-समय पर VACUUM ANALYZE (PostgreSQL) या OPTIMIZE TABLE (MySQL) चलाएं।

अक्सर पूछे जाने वाले सवाल (FAQs)

1. क्या हर कॉलम पर इंडेक्स बना देना सही है?

नहीं। इंडेक्स डेटाबेस में अतिरिक्त डिस्क स्पेस लेते हैं। जब भी आप टेबल में नया डेटा डालते हैं या अपडेट करते हैं, तो डेटाबेस को इंडेक्स भी अपडेट करना पड़ता है। इसलिए सिर्फ उन्हीं कॉलम्स पर इंडेक्स बनाएं जो WHERE, JOIN, या ORDER BY में अक्सर आते हैं।

2. B-Tree और Hash Index में क्या अंतर है?

B-Tree इंडेक्स रेंज क्वेरीज़ (जैसे >, <, BETWEEN) और सॉर्टिंग दोनों के लिए बेस्ट है। जबकि Hash Index केवल सटीक बराबरी (Exact Match जैसे =) के लिए बहुत तेज काम करता है, लेकिन यह रेंज सर्च में काम नहीं करता।

3. EXPLAIN और EXPLAIN ANALYZE में क्या फर्क है?

EXPLAIN सिर्फ डेटाबेस का अनुमानित प्लान दिखाता है बिना क्वेरी को असलियत में चलाए। वहीं EXPLAIN ANALYZE क्वेरी को असल में डेटाबेस पर रन करता है और सटीक समय व मेमोरी उपयोग की रिपोर्ट देता है।

Post a Comment

0 Comments