डेव्हलपर्स अनेकदा मला एकच प्रश्न विचारतात: "माझी API स्लो आहे. मी सुरुवात कुठून करू?" सहसा प्रतिक्रिया अशी असते की सर्व्हर अपग्रेड करा किंवा RAM दुप्पट करा. यामुळे खर्च वाढतो आणि मूळ समस्या सहजासहजी सुटत नाही. बहुतेक Laravel ॲप्लिकेशन्समध्ये, अडथळा (bottleneck) डेटाबेस लेयरमध्ये असतो. फ्रेमवर्कच्या सुटसुटीत सिंटॅक्समुळे आपण हे विसरतो की प्रत्येक Eloquent कॉल शेवटी SQL मध्ये रूपांतरित होतो आणि बहुतेक वेळा समस्या SQL मधूनच सुरू होते.
सर्व्हर सेटिंग्जमध्ये बदल करण्यापूर्वी, तुमच्या क्वेरीज (queries) पद्धतशीरपणे तपासा.
योग्य निदानापासून (Diagnostic) सुरुवात करा
अंधारात ऑप्टिमायझेशन करू नका. क्वेरीज विनाकारण बदलणे म्हणजे केवळ अंदाज लावणे आहे आणि अंदाजामुळे तासनतास वाया जातात.
तुम्हाला अशा स्टेटमेंट्स शोधण्याची गरज आहे ज्या सर्वाधिक एकूण वेळ घेतात. रिअल लोडमध्ये (real load) तुमच्या ॲप्लिकेशनचे निरीक्षण करा. Laravel Telescope तुम्हाला प्रत्येक रिक्वेस्ट दरम्यान एक्झिक्युट झालेल्या प्रत्येक क्वेरीचा वेळेसह स्पष्ट व्ह्यू देते. Laravel Debugbar तुमच्या लोकल डेव्हलपमेंट दरम्यान ब्राउझरमध्ये त्या समोर आणते, जेणेकरून तुम्ही त्वरित त्रुटी ओळखू शकाल. जेव्हा तुम्हाला प्रोडक्शनमध्ये समस्या शोधायच्या असतात, तेव्हा MySQL Slow Query Log सक्षम करा. हे तुम्ही ठरवलेल्या मर्यादेपेक्षा जास्त वेळ घेणाऱ्या स्टेटमेंट्स रेकॉर्ड करते, जे लहान डेटासेटमध्ये न दिसणाऱ्या समस्या शोधण्यासाठी आदर्श आहे. जर तुम्ही मोठ्या प्रमाणावर काही चालवत असाल, तर Application Performance Monitoring टूल स्लो HTTP एंडपॉइंट्सना विशिष्ट डेटाबेस कॉल्सशी जोडू शकते.
डेटा तपासताना दोन गोष्टींकडे लक्ष द्या: एक्झिक्यूशनचा एकूण वेळ (absolute execution time) आणि कॉलची वारंवारता (call frequency). चाळीस मिलीसेकंद घेणारी क्वेरी निरुपद्रवी वाटते, जोपर्यंत तुम्हाला हे समजत नाही की ती मिनिटाला दोन हजार वेळा चालते. तासाला एकदा चालणारा तीन सेकंदाचा रिपोर्ट, प्रत्येक पेजवर चालणाऱ्या अर्ध्या सेकंदाच्या लूकअपपेक्षा कमी महत्त्वाचा असू शकतो. प्रथम जास्त परिणाम करणाऱ्या समस्या सुधारा.
सर्व काही मागणे थांबवा
SELECT * सोयीचे आहे. पण ते महागडे (expensive) देखील आहे. जेव्हा तुम्ही Model::all() लिहिता किंवा कॉलमची नावे न सांगता रिझल्ट सेट मिळवता, तेव्हा MySQL प्रत्येक मॅच झालेल्या रो (row) साठी सर्व फील्ड्स खेचून आणते. यामध्ये मोठे टेक्स्ट फील्ड्स, JSON ब्लब्स आणि टेबलमधील इतर सर्व गोष्टींचा समावेश असतो. यामुळे रिझल्ट सेट वाढतो, मेमरीचा वापर वाढतो आणि रिस्पॉन्स सीरियलाईज (serializing) करण्यासाठी लागणारा वेळही वाढतो.
स्पष्ट राहा. जर तुमच्या कंट्रोलरला फक्त id, name, आणि email फील्ड्सची गरज असेल, तर फक्त तेच मागा:
User::select('id', 'name', 'email')->get();
क्वेरी बिल्डरमध्येही (query builder) हेच तत्व लागू होते. लहान पेलोड्स (payloads) नेटवर्कवर वेगाने प्रवास करतात आणि तुमच्या ॲप्लिकेशन सर्व्हरवर कमी RAM वापरतात. हे उपलब्ध असलेल्या सर्वात सोप्या आणि प्रभावी उपायांपैकी एक आहे, तरीही Laravel मध्ये SELECT * हे डीफॉल्ट वर्तन असल्याने अनेकदा ते दुर्लक्षित केले जाते.
तुमच्या बदलांसाठी EXPLAIN चा वापर करा
EXPLAIN न चालवता कधीही स्लो क्वेरी रिफॅक्टर (refactor) करू नका. MySQL मध्ये, EXPLAIN कीवर्ड क्वेरी एक्झिक्यूशन प्लॅन दाखवतो. ऑप्टिमायझर तुमचा डेटा शोधण्यासाठी नेमकी काय योजना आखत आहे, हे यातून स्पष्ट होते.
type कॉलमकडे लक्ष द्या. जर तुम्हाला ALL दिसत असेल, तर MySQL पूर्ण टेबल स्कॅन (full table scan) करत आहे. याचा अर्थ असा की तुमच्या WHERE क्लॉजसाठी ते प्रत्येक रो वाचत आहे. ऑप्टिमायझर इंडेक्स वापरत आहे की नाही हे पाहण्यासाठी key कॉलम तपासा. त्यानंतर Extra कॉलम तपासा. जर तुम्हाला Using temporary किंवा Using filesort दिसले, तर याचा अर्थ असा की तुमची सध्याची रचना क्वेरी व्यवस्थित पूर्ण करू शकत नाहीये, म्हणून MySQL तात्पुरते टेबल्स बनवत आहे किंवा मेमरीमध्ये सॉर्टिंग करत आहे.
तुमच्या MySQL क्लायंटमध्ये EXPLAIN चालवा, किंवा आउटपुट फॉरमॅट करून देणारे टूल वापरा. एकदा तुम्हाला प्लॅन समजला की, समस्या गहाळ इंडेक्समुळे आहे, चुकीच्या जॉइनमुळे (bad join) आहे की इंजिन ऑप्टिमाइझ करू न शकणाऱ्या प्रेडिकेटमुळे (predicate) आहे, हे तुम्हाला समजेल. अंदाज लावण्याची गरज उरणार नाही.
उद्देशपूर्ण इंडेक्सिंग करा
लूकअप्सचा वेग वाढवण्यासाठी इंडेक्स हे सर्वात शक्तिशाली साधन आहे, परंतु ते तुम्ही ज्या पद्धतीने क्वेरी करता त्याशी जुळले तरच काम करतात. योग्य इंडेक्सशिवाय, MySQL प्रत्येक रो एक-एक करून स्कॅन करते. हजारो रो असलेल्या टेबलवर डेव्हलपमेंट दरम्यान हे ठीक वाटू शकते, परंतु दहा दशलक्ष रो असलेल्या टेबलवर प्रोडक्शनमध्ये ते कोलमडून पडू शकते.
WHERE क्लॉजमध्ये वारंवार येणाऱ्या फील्ड्ससाठी सिंगल-कॉलम इंडेक्सपासून सुरुवात करा. जर तुम्ही सतत status नुसार फिल्टर करत असाल, तर status वर इंडेक्स जोडा.
जेव्हा एखादी क्वेरी एकाच वेळी अनेक कॉलम्सवर फिल्टर करते, तेव्हा कंपोझिट इंडेक्सकडे (composite indexes) वळा. इंडेक्समधील कॉलम्सचा क्रम महत्त्वाचा असतो कारण MySQL कंपोझिट इंडेक्स डावीकडून उजवीकडे वाचते. याला 'लेफ्टमोस्ट प्रीफिक्स रूल' (leftmost prefix rule) म्हणतात. जर तुमची क्वेरी user_id ने शोधत असेल आणि नंतर created_at नुसार क्रम लावत असेल, तर (user_id, created_at) वरील कंपोझिट इंडेक्स खूप मदत करतो. क्रम उलट केल्यास ऑप्टिमायझर फिल्टरसाठी इंडेक्स कदाचित वापरणार नाही.
प्रत्येक कॉलमला इंडेक्स करू नका. प्रत्येक इंडेक्समुळे इन्सर्ट (insert), अपडेट (update) आणि डिलीट (delete) प्रक्रियेवर अतिरिक्त भार (overhead) पडतो कारण MySQL ला त्याची रचना टिकवून ठेवावी लागते. मोजमाप करताना तुम्हाला सापडलेल्या पॅटर्ननुसार ते विचारपूर्वक जोडा.
कॉलम्सपासून फंक्शन्स दूर ठेवा
ही चूक नकळत इंडेक्स अक्षम करते. जेव्हा तुम्ही WHERE क्लॉजमध्ये एखाद्या कॉलमला फंक्शनमध्ये गुंडाळता, तेव्हा MySQL...
