ഡെവലപ്പർമാർ പലപ്പോഴും എന്നോട് ഒരേ ചോദ്യം ചോദിക്കാറുണ്ട്: "എന്റെ API പതുക്കെയാണ്. ഞാൻ എവിടെ നിന്നാണ് തുടങ്ങേണ്ടത്?" സാധാരണയായി സർവർ അപ്‌ഗ്രേഡ് ചെയ്യാനോ അല്ലെങ്കിൽ RAM ഇരട്ടിയാക്കാനോ ആണ് ആളുകൾ ആദ്യം ശ്രമിക്കാറുള്ളത്. എന്നാൽ അതിന് വലിയ ചിലവ് വരും, കൂടാതെ അത് പലപ്പോഴും പ്രശ്നത്തിന്റെ യഥാർത്ഥ കാരണം പരിഹരിക്കാറുമില്ല. മിക്ക Laravel ആപ്ലിക്കേഷനുകളിലും, തടസ്സങ്ങൾ (bottleneck) ഉണ്ടാകുന്നത് ഡാറ്റാബേസ് ലെയറിലാണ്. ഫ്രെയിംവർക്കിന്റെ ലളിതമായ സിന്റാക്സ് കാരണം, ഓരോ Eloquent കോൾ deയും ഒടുവിൽ SQL ആയി മാറുന്നുവെന്നും, പലപ്പോഴും പ്രശ്നങ്ങൾ തുടങ്ങുന്നത് SQL-ൽ ആണെന്നും നാം മറന്നുപോകാറുണ്ട്.

ഒരു സെർവർ സെറ്റിംഗിലും മാറ്റം വരുത്തുന്നതിന് മുമ്പ്, നിങ്ങളുടെ ക്വറികൾ (queries) കൃത്യമായ രീതിയിൽ പരിശോധിക്കുക.

ശരിയായ രോഗനിർണ്ണയത്തിലൂടെ (Diagnostic) തുടങ്ങുക

അറിവില്ലാതെ ഒപ്റ്റിമൈസ് ചെയ്യാൻ ശ്രമിക്കരുത്. ക്വറികൾ വെറുതെ മാറ്റിക്കൊണ്ടിരിക്കുന്നത് ഊഹങ്ങൾക്കടിസ്ഥാനമാക്കിയുള്ള പ്രവൃത്തിയാണ്, ഇത് സമയം വെറുതെ കളയാൻ മാത്രമേ സഹായിക്കൂ.

ഏറ്റവും കൂടുതൽ സമയം എടുക്കുന്ന സ്റ്റേറ്റ്‌മെന്റുകൾ കണ്ടെത്തേണ്ടതുണ്ട്. യഥാർത്ഥ ലോഡിൽ (real load) നിങ്ങളുടെ ആപ്ലിക്കേഷൻ എങ്ങനെ പ്രവർത്തിക്കുന്നു എന്ന് നിരീക്ഷിക്കുക. ഒരു റിക്വസ്റ്റ് സമയത്ത് നടപ്പിലാക്കുന്ന ഓരോ ക്വറിയും അതിന്റെ സമയത്തോടൊപ്പം കാണാൻ Laravel Telescope സഹായിക്കുന്നു. ലോക്കൽ ഡെവലപ്‌മെന്റ് സമയത്ത് ബ്രൗസറിൽ തന്നെ ഇവ കാണാൻ Laravel Debugbar സഹായിക്കുന്നു, അതുവഴി പ്രശ്നങ്ങൾ പെട്ടെന്ന് തിരിച്ചറിയാം. പ്രൊഡക്ഷനിൽ (production) പ്രശ്നങ്ങൾ കണ്ടെത്താൻ MySQL Slow Query Log ഉപയോഗിക്കുക. നിങ്ങൾ നിശ്ചയിച്ചിട്ടുള്ള പരിധിയിൽ കൂടുതൽ സമയം എടുക്കുന്ന സ്റ്റേറ്റ്‌മെന്റുകൾ ഇത് രേഖപ്പെടുത്തുന്നു. ചെറിയ ഡാറ്റാസെറ്റുകളിൽ കാണാത്ത പ്രശ്നങ്ങൾ കണ്ടെത്താൻ ഇത് വളരെ ഉപകാരപ്രദമാണ്. വലിയ ആപ്ലിക്കേഷനുകൾ ആണെങ്കിൽ, ഒരു Application Performance Monitoring ടൂൾ ഉപയോഗിച്ച് പതുക്കെ പ്രവർത്തിക്കുന്ന HTTP എൻഡ്‌പോയിന്റുകളെയും അവയുമായി ബന്ധപ്പെട്ട ഡാറ്റാബേസ് കോളുകളെയും തമ്മിൽ ബന്ധിപ്പിച്ചു കണ്ടെത്താം.

ഡാറ്റ പരിശോധിക്കുമ്പോൾ രണ്ട് കാര്യങ്ങൾ ശ്രദ്ധിക്കുക: എക്സിക്യൂഷൻ സമയം (execution time), കോൾ ഫ്രീക്വൻസി (call frequency). നാല്പത് മില്ലിസെക്കൻഡ് മാത്രം എടുക്കുന്ന ഒരു ക്വറി വലിയ പ്രശ്നമല്ലെന്ന് തോന്നാം, എന്നാൽ അത് മിനിറ്റിൽ രണ്ടായിരം തവണ പ്രവർത്തിക്കുന്നുണ്ടെങ്കിൽ അത് വലിയൊരു പ്രശ്നമാണ്. മണിക്കൂറിൽ ഒരിക്കൽ മാത്രം പ്രവർത്തിക്കുന്ന മൂന്ന് സെക്കൻഡ് എടുക്കുന്ന ഒരു റിപ്പോർട്ടിനേക്കാൾ, ഓരോ പേജിലും പ്രവർത്തിക്കുന്ന അര സെക്കൻഡ് എടുക്കുന്ന ഒരു ലുക്കപ്പ് (lookup) ആണ് കൂടുതൽ ശ്രദ്ധിക്കേണ്ടത്. അതുകൊണ്ട്, കൂടുതൽ ആഘാതം ഉണ്ടാക്കുന്ന പ്രശ്നങ്ങൾ ആദ്യം പരിഹരിക്കുക.

എല്ലാം ചോദിച്ചു വാങ്ങുന്നത് നിർത്തുക

SELECT * ഉപയോഗിക്കുന്നത് എളുപ്പമാണ്, എന്നാൽ അത് ചിലവേറിയതാണ്. നിങ്ങൾ Model::all() എന്ന് എഴുതുകയോ കോളങ്ങളുടെ പേര് പറയാതെ റിസൾട്ട് സെറ്റ് എടുക്കുകയോ ചെയ്യുമ്പോൾ, ഓരോ വരിയിലെയും എല്ലാ ഫീൽഡുകളും MySQL വലിച്ചെടുക്കുന്നു. ഇതിൽ വലിയ ടെക്സ്റ്റ് ഫീൽഡുകൾ, JSON ബ്ലോബുകൾ എന്നിവയും ഉൾപ്പെടാം. ഇത് റിസൾട്ട് സെറ്റിന്റെ വലിപ്പം കൂട്ടുകയും മെമ്മറി ഉപയോഗം വർദ്ധിപ്പിക്കുകയും ചെയ്യും, കൂടാതെ റെസ്‌പോൺസ് സെരിയലൈസ് (serialize) ചെയ്യാനും കൂടുതൽ സമയം എടുക്കും.

കൃത്യമായി ആവശ്യപ്പെടുക. നിങ്ങളുടെ കൺട്രോളറിന് 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 ക്ലോസ് നിറവേറ്റാൻ ഓരോ വരിയും അത് വായിക്കുന്നു. ഒപ്റ്റിമൈസർ ഒരു ഇൻഡക്സ് (index) ഉപയോഗിക്കുന്നുണ്ടോ എന്ന് അറിയാൻ key കോളം പരിശോധിക്കുക. തുടർന്ന് Extra കോളം പരിശോധിക്കുക. അവിടെ Using temporary അല്ലെങ്കിൽ Using filesort എന്ന് കാണുന്നുണ്ടെങ്കിൽ, നിങ്ങളുടെ നിലവിലെ ഘടനയ്ക്ക് ക്വറി കൃത്യമായി കൈകാര്യം ചെയ്യാൻ കഴിയാത്തതിനാൽ MySQL ഇടക്കാല ടേബിളുകൾ നിർമ്മിക്കുകയോ മെമ്മറിയിൽ സോർട്ട് ചെയ്യുകയോ ചെയ്യുന്നു എന്നാണ് അർത്ഥം.

നിങ്ങളുടെ MySQL ക്ലയന്റിൽ EXPLAIN റൺ ചെയ്യുക, അല്ലെങ്കിൽ ഔട്ട്‌പുട്ട് ഫോർമാറ്റ് ചെയ്ത് നൽകുന്ന ഒരു ടൂൾ ഉപയോഗിക്കുക. പ്ലാൻ കണ്ടുകഴിഞ്ഞാൽ, പ്രശ്നം ഒരു ഇൻഡക്സ് ഇല്ലാത്തതാണോ, മോശം ജോയിൻ (join) ആണോ, അതോ എൻജിന് ഒപ്റ്റിമൈസ് ചെയ്യാൻ കഴിയാത്ത ഒരു പ്രെഡിക്കേറ്റ് (predicate) ആണോ എന്ന് നിങ്ങൾക്ക് മനസ്സിലാകും. അപ്പോൾ ഊഹിക്കേണ്ട ആവശ്യം വരുന്നില്ല.

കൃത്യമായ ലക്ഷ്യത്തോടെ ഇൻഡക്സ് ചെയ്യുക

ലുക്കപ്പുകൾ (lookups) വേഗത്തിലാക്കാനുള്ള ഏറ്റവും ശക്തമായ മാർഗമാണ് ഇൻഡക്സുകൾ, എന്നാൽ അവ നിങ്ങളുടെ ക്വറികളുമായി പൊരുത്തപ്പെട്ടാൽ മാത്രമേ ഫലപ്രദമാകൂ. ശരിയായ ഇൻഡക്സ് ഇല്ലെങ്കിൽ, MySQL ഓരോ വരിയായി പരിശോധിക്കും. ആയിരം വരികളുള്ള ഒരു ടേബിളിൽ ഡെവലപ്‌മെന്റ് സമയത്ത് ഇത് കുഴപ്പമില്ലെന്ന് തോന്നാം, എന്നാൽ പത്ത് ദശലക്ഷം വരികളുള്ള ഒരു ടേബിളിൽ പ്രൊഡക്ഷനിൽ ഇത് വലിയ തകർച്ചയ്ക്ക് കാരണമാകും.

WHERE ക്ലോസുകളിൽ ഇടയ്ക്കിടെ ഉപയോഗിക്കുന്ന ഫീൽഡുകൾക്കായി സിംഗിൾ-കോളൻ ഇൻഡക്സുകൾ (single-column indexes) ഉപയോഗിച്ച് തുടങ്ങുക. നിങ്ങൾ എപ്പോഴും status ഉപയോഗിച്ച് ഫിൽട്ടർ ചെയ്യുന്നുണ്ടെങ്കിൽ, status-ൽ ഒരു ഇൻഡക്സ് ചേർക്കുക.

ഒരു ക്വറി ഒന്നിലധികം കോളങ്ങൾ ഉപയോഗിച്ച് ഫിൽട്ടർ ചെയ്യുന്നുണ്ടെങ്കിൽ, കോമ്പോസിറ്റ് ഇൻഡക്സുകളിലേക്ക് (composite indexes) മാറാം. ഇൻഡക്സിനുള്ളിലെ കോളങ്ങളുടെ ക്രമം പ്രധാനമാണ്, കാരണം MySQL കോമ്പോസിറ്റ് ഇൻഡക്സുകൾ ഇടത്തുനിന്ന് വലത്തോട്ട് ആണ് വായിക്കുന്നത്. ഇതിനെ 'leftmost prefix rule' എന്ന് വിളിക്കുന്നു. നിങ്ങളുടെ ക്വറി user_id ഉപയോഗിച്ച് തിരയുകയും തുടർന്ന് created_at ഉപയോഗിച്ച് ക്രമീകരിക്കുകയും (order) ചെയ്യുന്നുണ്ടെങ്കിൽ, (user_id, created_at) എന്ന കോമ്പോസിറ്റ് ഇൻഡക്സ് വലിയ രീതിയിൽ സഹായിക്കും. ക്രമം തിരിച്ചാണെങ്കിൽ ഒപ്റ്റിമൈസർ ഫിൽട്ടറിനായി ഇൻഡക്സ് ഉപയോഗിച്ചേക്കില്ല.

എല്ലാ കോളങ്ങളും ഇൻഡക്സ് ചെയ്യരുത്. ഓരോ ഇൻഡക്സും ഇൻസേർട്ട് (insert), അപ്‌ഡേറ്റ് (update), ഡിലീറ്റ് (delete) എന്നിവയുടെ വേഗത കുറയ്ക്കും, കാരണം MySQL ആ ഘടന നിലനിർത്തേണ്ടതുണ്ട്. അളന്നുനോക്കിയതിൽ നിന്ന് ലഭിച്ച പാറ്റേണുകൾ അടിസ്ഥാനമാക്കി മാത്രം അവ ചേർക്കുക.

ഫംഗ്ഷനുകളെ കോളങ്ങളിൽ നിന്ന് അകറ്റി നിർത്തുക

ഈ തെറ്റ് ഇൻഡക്സുകളെ അറിയാതെ തന്നെ പ്രവർത്തനരഹിതമാക്കുന്നു. ഒരു WHERE ക്ലോസിനുള്ളിൽ ഒരു കോളം ഒരു ഫങ്ക്ഷനിൽ ഉൾപ്പെടുത്തുമ്പോൾ, MySQL-ന് സാധിക്കില്ല