ایک غیر انڈیکس شدہ کالم نے ہمارے ڈیٹا بیس کو کیسے تباہ کر دیا
ایک واحد غائب انڈیکس نے 8 ms کی API کو 8 سیکنڈ کے ڈراؤنے خواب میں بدل دیا۔
ہم نے ایک بڑی ریلیز سے پہلے لوڈ ٹیسٹ (load test) کیا۔ لوکل ڈویلپمنٹ 100 ریکارڈز کے ساتھ بالکل ٹھیک کام کر رہی تھی۔ پھر ہم نے پروڈکشن کے حجم کے ڈیٹا میں 1.5 ملین rows پر 500 صارفین کی نقل (simulate) کی۔
نتائج بہت خراب تھے:
- API رسپانس ٹائم 45 ms سے بڑھ کر 8,000 ms ہو گیا۔
- ڈیٹا بیس کا CPU 100% تک پہنچ گیا۔
- کنکشن پولز (connection pools) ختم ہو گئے، جس کی وجہ سے ٹائم آؤٹ ایررز (timeout errors) آنے لگے۔
ہم نے سلو-کویری لاگ (slow-query log) چیک کیا اور ہمیں ایک ایسا اینڈ پوائنٹ ملا جو user ID اور status کے ذریعے آرڈر ہسٹری حاصل کر رہا تھا۔
PostgreSQL پر EXPLAIN ANALYZE چلانے سے ایک Sequential Scan ظاہر ہوا۔ انجن ہر درخواست کے لیے تمام 1.5 ملین rows پڑھ رہا تھا کیونکہ user_id پر کوئی انڈیکس نہیں تھا۔
100 بیک وقت (concurrent) درخواستوں کے ساتھ، اس نے ایک ہی وقت میں 150 ملین rows اسکین کیں۔
اس کا حل نکالنے میں صرف پانچ منٹ لگے۔
user_id پر ایک سادہ انڈیکس کے بجائے، ہم نے (user_id, status, created_at DESC) پر ایک کمپوزٹ انڈیکس (composite index) بنایا۔ اس سے ڈیٹا بیس کو یہ سہولت ملی:
user_idکے ذریعے فلٹر کرنا۔statusکے ذریعے فلٹر کرنا۔- فوری طور پر تازہ ترین rows واپس کرنا۔
- اضافی سورٹنگ (sorting) کے مراحل کو چھوڑنا۔
ہم نے CREATE INDEX CONCURRENTLY کا استعمال کیا تاکہ آپریشن کے دوران ٹیبل ان لاک (unlocked) رہے۔
حل کے بعد:
- کوئری کا وقت 8,150 ms سے کم ہو کر 0.14 ms رہ گیا۔
- API لیٹنسی (latency) 8 سیکنڈ سے کم ہو کر 12 ms ہو گئی۔
- CPU کا استعمال 100% سے گر کر 8% سے بھی کم ہو گیا۔
سیکھے گئے اسباق:
- لوکل ٹیسٹنگ گمراہ کن ہو سکتی ہے؛ 100 rows لاکھوں کی نمائندگی نہیں کرتیں۔
- اپنی فارن کیز (foreign keys) کو انڈیکس کریں—زیادہ تر ORMs اسے نظر انداز کر دیتے ہیں۔
- مزید ہارڈ ویئر خریدنے سے پہلے
EXPLAIN ANALYZEچلائیں۔ - انڈیکس کو ان کوئریز کے گرد ڈیزائن کریں جو آپ حقیقت میں چلاتے ہیں۔
پہلے سرورز کو اسکیل (scale) نہ کریں۔ کوئریز کو اسکیل کریں۔
