ڈویلپرز اکثر مجھ سے ایک ہی سوال پوچھتے ہیں: "میری API سست ہے۔ میں کہاں سے شروع کروں؟" عام ردعمل سرور کو اپ گریڈ کرنا یا RAM کو دوگنا کرنا ہوتا ہے۔ اس سے پیسے خرچ ہوتے ہیں اور یہ شاذ و نادر ہی اصل وجہ کو حل کرتا ہے۔ زیادہ تر Laravel ایپلی کیشنز میں، رکاوٹ (bottleneck) ڈیٹا بیس لیئر میں ہوتی ہے۔ فریم ورک کا خوبصورت سنٹیکس (syntax) یہ بھولنا آسان بنا دیتا ہے کہ ہر Eloquent کال آخر کار SQL بن جاتی ہے، اور اکثر SQL ہی وہ جگہ ہے جہاں سے مسائل شروع ہوتے ہیں۔
سرور کی سیٹنگز کو چھونے سے پہلے، اپنی کوئریز (queries) کا طریقہ کار سے جائزہ لیں۔
صحیح تشخیص سے آغاز کریں
اندھیرے میں آپٹیمائزیشن نہ کریں۔ کوئریز کو بلاوجہ دوبارہ لکھنا محض ایک اندازہ ہے، اور اندازے گھنٹوں ضائع کرتے ہیں۔
آپ کو ان سٹیٹمنٹس (statements) کو تلاش کرنے کی ضرورت ہے جو کل طور پر سب سے زیادہ وقت لیتی ہیں۔ اپنی ایپلی کیشن کو حقیقی لوڈ (real load) کے تحت دیکھیں۔ Laravel Telescope آپ کو ہر اس کوئری کا صاف منظر فراہم کرتا ہے جو ایک ریکویسٹ کے دوران چلائی گئی ہو، ساتھ ہی اس کا وقت (timing) بھی منسلک ہوتا ہے۔ Laravel Debugbar لوکل ڈویلپمنٹ کے دوران آپ کے براؤزر میں انہیں ظاہر کرتا ہے تاکہ آپ فوری طور پر غیر معمولی چیزوں کو پہچان سکیں۔ جب آپ کو پروڈکشن میں مسائل پکڑنے کی ضرورت ہو، تو MySQL Slow Query Log کو فعال کریں۔ یہ ان سٹیٹمنٹس کو ریکارڈ کرتا ہے جو آپ کے طے کردہ حد (threshold) سے تجاوز کرتی ہیں، جو اسے ان حیران کن مسائل کو تلاش کرنے کے لیے مثالی بناتا ہے جو چھوٹے ڈیٹا سیٹس میں ظاہر نہیں ہوتے۔ اگر آپ کچھ بڑا چلا رہے ہیں، تو Application Performance Monitoring ٹول سست HTTP endpoints کو مخصوص ڈیٹا بیس کالز کے ساتھ جوڑ سکتا ہے۔
جیسے جیسے آپ ڈیٹا کا جائزہ لیں، دو چیزیں تلاش کریں: ایگزیکیوشن کا کل وقت (absolute execution time) اور کال کی فریکوئنسی (call frequency)۔ چالیس ملی سیکنڈ لینے والی کوئری بے ضرر لگتی ہے جب تک کہ آپ کو یہ احساس نہ ہو کہ یہ ہر منٹ میں دو ہزار بار چلتی ہے۔ ایک تین سیکنڈ کی رپورٹ جو گھنٹے میں ایک بار چلتی ہے، شاید اس آدھے سیکنڈ کے لک اپ (lookup) سے کم اہم ہو جو ہر پیج پر چلتا ہے۔ پہلے زیادہ اثر ڈالنے والے مسائل کو حل کریں۔
سب کچھ مانگنا بند کریں
SELECT * سہولت بخش ہے۔ لیکن یہ مہنگا بھی ہے۔ جب آپ Model::all() لکھتے ہیں یا کالموں کے نام بتائے بغیر رزلٹ سیٹ حاصل کرتے ہیں، تو MySQL ہر میچ ہونے والی رو (row) کے لیے ہر فیلڈ کو اٹھاتا ہے۔ اس میں بڑے ٹیکسٹ فیلڈز، JSON blobs، اور ٹیبل پر موجود کوئی بھی دوسری چیز شامل ہوتی ہے۔ رزلٹ سیٹ بڑھ جاتا ہے، میموری کا استعمال بڑھ جاتا ہے، اور ریسپانس کو سیریلائز (serializing) کرنے میں لگنے والا وقت بڑھ جاتا ہے۔
واضح رہیں۔ اگر آپ کے کنٹرولر کو صرف id، name اور email فیلڈز کی ضرورت ہے، تو صرف وہی طلب کریں:
User::select('id', 'name', 'email')->get();
Query builder میں بھی یہی اصول لاگو ہوتا ہے۔ چھوٹے پے لوڈز (payloads) نیٹ ورک پر تیزی سے منتقل ہوتے ہیں اور آپ کے ایپلی کیشن سرور پر کم RAM استعمال کرتے ہیں۔ یہ دستیاب سب سے سستے حل میں سے ایک ہے، پھر بھی اسے نظر انداز کرنا آسان ہے کیونکہ Laravel SELECT * کو ڈیفالٹ رویہ بناتا ہے۔
EXPLAIN کو اپنی تبدیلیوں کے لیے رہنما بنائیں
پہلے EXPLAIN چلائے بغیر کبھی بھی کسی سست کوئری کو ریفیکٹر (refactor) نہ کریں۔ MySQL میں، EXPLAIN کی ورڈ کوئری کے ایگزیکیوشن پلان (execution plan) کو ظاہر کرتا ہے۔ یہ بالکل واضح کرتا ہے کہ آپٹیمائزر آپ کے ڈیٹا کو تلاش کرنے کے لیے کیا ارادہ رکھتا ہے۔
type کالم پر توجہ دیں۔ اگر آپ کو ALL نظر آتا ہے، تو MySQL مکمل ٹیبل اسکین (full table scan) کر رہا ہے۔ اس کا مطلب ہے کہ وہ آپ کے WHERE کلاز کو پورا کرنے کے لیے ہر رو کو پڑھ رہا ہے۔ یہ دیکھنے کے لیے کہ آیا آپٹیمائزر انڈیکس (index) استعمال کر رہا ہے یا نہیں، key کالم کو دیکھیں۔ پھر Extra کالم کو چیک کریں۔ اگر آپ کو Using temporary یا Using filesort نظر آئے، تو اس کا مطلب ہے کہ MySQL درمیانی ٹیبلز بنا رہا ہے یا میموری میں سورٹنگ کر رہا ہے کیونکہ آپ کا موجودہ ڈھانچہ کوئری کو آسانی سے پورا نہیں کر سکتا۔
اپنے MySQL کلائنٹ میں EXPLAIN چلائیں، یا ایسا ٹول استعمال کریں جو آپ کے لیے آؤٹ پٹ کو فارمیٹ کر دے۔ ایک بار جب آپ پلان دیکھ لیں، تو آپ کو معلوم ہو جائے گا کہ مسئلہ گمشدہ انڈیکس کا ہے، ایک خراب جوائن (join) کا ہے، یا کسی ایسے پریڈیکیٹ (predicate) کا ہے جسے انجن آپٹیمائز نہیں کر سکتا۔ اندازہ لگانا غیر ضروری ہو جاتا ہے۔
ارادے کے ساتھ انڈیکس کریں
انڈیکس لک اپس (lookups) کو تیز کرنے کے لیے سب سے طاقتور ٹول ہیں، لیکن یہ صرف تب کام کرتے ہیں جب وہ آپ کے کوئری کرنے کے طریقے سے مطابقت رکھتے ہوں۔ صحیح انڈیکس کے بغیر، MySQL ایک ایک کر کے رو اسکین کرتا ہے۔ ڈویلپمنٹ میں ایک ہزار روز والے ٹیبل پر یہ ٹھیک لگ سکتا ہے، لیکن پروڈکشن میں دس ملین روز والے ٹیبل پر یہ ناکام ہو سکتا ہے۔
ان فیلڈز کے لیے سنگل کالم انڈیکس سے شروع کریں جو WHERE کلاز میں کثرت سے ظاہر ہوتے ہیں۔ اگر آپ مسلسل status کے ذریعے فلٹر کرتے ہیں، تو status پر انڈیکس شامل کریں۔
جب ایک کوئری مل کر کئی کالموں پر فلٹر کرتی ہے، تو کمپوزٹ انڈیکسز (composite indexes) کی طرف بڑھیں۔ انڈیکس کے اندر کالموں کی ترتیب اہمیت رکھتی ہے کیونکہ MySQL کمپوزٹ انڈیکسز کو بائیں سے دائیں پڑھتا ہے۔ اسے leftmost prefix rule کہا جاتا ہے۔ اگر آپ کی کوئری user_id کے ذریعے تلاش کرتی ہے اور پھر created_at کے ذریعے آرڈر کرتی ہے، تو (user_id, created_at) پر کمپوزٹ انڈیکس نمایاں طور پر مدد کرتا ہے۔ ترتیب الٹ دیں اور آپٹیمائزر فلٹر کے لیے انڈیکس استعمال نہیں کرے گا۔
ہر کالم پر انڈیکس نہ لگائیں۔ ہر انڈیکس انسرٹس (inserts)، اپ ڈیٹس (updates) اور ڈیلیٹس (deletes) پر اضافی بوجھ (overhead) ڈالتا ہے کیونکہ MySQL کو اس ڈھانچے کو برقرار رکھنا پڑتا ہے۔ انہیں جان بوجھ کر ان پیٹرنز کی بنیاد پر شامل کریں جو آپ نے پیمائش کے دوران پائے ہیں۔
فنکشنز کو کالموں سے دور رکھیں
یہ غلطی خاموشی سے انڈیکس (indexes) کو غیر فعال کر دیتی ہے۔ جب آپ WHERE کلاز کے اندر کسی کالم کو فنکشن میں استعمال کرتے ہیں، تو MySQL نہیں کر سکتا
