Laravel Eloquent: Tips Query Database agar Aplikasi Lebih Cepat
Laravel Eloquent membuat proses berinteraksi dengan database menjadi lebih mudah melalui syntax ORM yang ekspresif. Developer dapat mengambil data, membuat relasi, melakukan filtering, pagination, hingga update database tanpa harus menulis SQL secara manual untuk setiap operasi.
Namun, kemudahan Eloquent tidak otomatis membuat query database menjadi cepat. Query yang tidak efisien, pengambilan data berlebihan, relasi yang memicu N+1 query, serta kurangnya index database dapat menyebabkan aplikasi Laravel melambat ketika jumlah data semakin besar.
Pada artikel ini, kita akan membahas berbagai tips Laravel Eloquent untuk mempercepat query database, mulai dari memilih kolom yang diperlukan, menggunakan eager loading, pagination, exists(), count(), chunking, indexing, hingga cara menganalisis query yang lambat.
Mengapa Optimasi Query Laravel Penting?
Ketika aplikasi masih memiliki sedikit data, query yang kurang optimal sering kali tidak terlihat.
Misalnya:
$users = User::all();
Jika tabel users hanya memiliki 20 data, query tersebut mungkin tidak menimbulkan masalah.
Tetapi jika jumlahnya sudah ratusan ribu atau jutaan record, mengambil seluruh kolom dan seluruh data sekaligus dapat menggunakan banyak memory dan memperlambat response.
Optimasi query bertujuan untuk:
- Mengurangi jumlah query ke database.
- Mengurangi data yang dikirim dari database.
- Mengurangi penggunaan memory PHP.
- Memanfaatkan index database.
- Menghindari query yang tidak diperlukan.
- Mempercepat response aplikasi.
- Membuat aplikasi lebih siap menghadapi pertumbuhan data.
Optimasi tidak selalu berarti membuat query menjadi lebih kompleks. Sering kali, perubahan sederhana sudah memberikan dampak besar.
1. Jangan Gunakan all() Jika Tidak Membutuhkan Semua Data
Salah satu kesalahan umum adalah menggunakan:
$users = User::all();
Query tersebut mengambil seluruh record dan seluruh kolom dari tabel.
Jika aplikasi hanya membutuhkan user aktif, lebih baik gunakan filter:
$users = User::where('status', 'active')->get();
Jika hanya membutuhkan beberapa kolom:
$users = User::where('status', 'active')
->select(['id', 'name', 'email'])
->get();
Semakin sedikit data yang diambil, semakin kecil pekerjaan yang harus dilakukan database dan aplikasi.
2. Gunakan select() untuk Mengambil Kolom yang Dibutuhkan
Hindari mengambil seluruh kolom jika hanya membutuhkan sebagian data.
Kurang efisien:
$users = User::get();
Lebih baik:
$users = User::select('id', 'name', 'email')->get();
Atau:
$users = User::query()
->select(['id', 'name'])
->where('status', 'active')
->get();
Ini sangat berguna pada tabel yang memiliki banyak kolom atau menyimpan data berukuran besar seperti JSON, text, atau blob.
3. Gunakan find() untuk Mencari Berdasarkan Primary Key
Jika pencarian berdasarkan primary key, gunakan find():
$user = User::find($id);
Jika data tidak ditemukan:
$user = User::find($id);
if (! $user) {
abort(404);
}
Untuk kebutuhan tertentu, gunakan findOrFail():
$user = User::findOrFail($id);
findOrFail() akan menghasilkan exception ketika record tidak ditemukan.
4. Gunakan exists() untuk Mengecek Keberadaan Data
Jika hanya ingin mengetahui apakah data tersedia, jangan mengambil seluruh record.
Kurang efisien:
$user = User::where('email', $email)->first();
if ($user) {
// User tersedia
}
Jika hanya membutuhkan informasi ada atau tidak:
if (User::where('email', $email)->exists()) {
// User tersedia
}
exists() cocok untuk validasi dan pengecekan kondisi karena aplikasi tidak perlu mengambil seluruh object model.
5. Gunakan count() untuk Menghitung Data
Jika hanya membutuhkan jumlah record, gunakan count():
$totalUsers = User::where('status', 'active')->count();
Hindari:
$totalUsers = User::where('status', 'active')->get()->count();
Versi kedua mengambil data terlebih dahulu ke aplikasi, kemudian menghitungnya. count() langsung meminta database melakukan operasi penghitungan.
6. Gunakan sum(), avg(), min(), dan max()
Operasi agregasi sebaiknya dilakukan oleh database.
Contoh:
$totalRevenue = Order::where('status', 'paid')->sum('total');
Rata-rata:
$averageOrder = Order::where('status', 'paid')->avg('total');
Nilai terbesar:
$highestOrder = Order::where('status', 'paid')->max('total');
Database biasanya jauh lebih efisien untuk operasi agregasi dibanding mengambil seluruh data lalu menghitungnya menggunakan PHP.
7. Hindari Masalah N+1 Query
N+1 query merupakan salah satu masalah performa yang sering terjadi ketika menggunakan Eloquent relationship.
Misalnya:
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name;
}
Query pertama mengambil posts:
SELECT * FROM posts
Kemudian setiap $post->user dapat memicu query tambahan.
Jika terdapat 100 post, aplikasi berpotensi menjalankan satu query untuk posts ditambah banyak query untuk user.
Inilah yang dikenal sebagai N+1 query problem.
8. Gunakan Eager Loading dengan with()
Solusinya adalah eager loading:
$posts = Post::with('user')->get();
Kemudian:
foreach ($posts as $post) {
echo $post->user->name;
}
Dengan eager loading, Laravel mengambil relationship secara lebih efisien.
Untuk beberapa relationship:
$posts = Post::with(['user', 'comments'])->get();
Jika relationship memiliki nested relationship:
$posts = Post::with('comments.user')->get();
9. Batasi Kolom Relationship
Eager loading juga dapat mengambil kolom tertentu.
Contoh:
$posts = Post::with('user:id,name')->get();
Ini berguna jika Anda hanya membutuhkan informasi dasar user.
Namun, pastikan foreign key yang diperlukan relationship tetap tersedia.
Misalnya:
$posts = Post::with('user:id,name')->get();
Jika relationship membutuhkan user_id pada model Post, kolom tersebut tetap harus tersedia pada query utama.
10. Gunakan Lazy Eager Loading Jika Dibutuhkan Setelah Query
Terkadang relationship baru diketahui diperlukan setelah model diperoleh.
Gunakan load():
$posts = Post::latest()->get();
$posts->load('user');
Untuk kondisi tertentu:
$posts->load([
'user',
'comments',
]);
Perbedaannya secara sederhana:
with()digunakan saat membangun query.load()digunakan setelah model atau collection diperoleh.
11. Gunakan withCount() untuk Menghitung Relationship
Misalnya setiap post memiliki banyak komentar.
Daripada mengambil seluruh komentar hanya untuk menghitung jumlahnya:
$posts = Post::with('comments')->get();
foreach ($posts as $post) {
echo $post->comments->count();
}
Gunakan:
$posts = Post::withCount('comments')->get();
Kemudian:
echo $post->comments_count;
Laravel akan menambahkan hasil perhitungan ke setiap model.
Anda juga dapat menggunakan kondisi:
$posts = Post::withCount([
'comments' => function ($query) {
$query->where('approved', true);
},
])->get();
12. Gunakan withExists() untuk Mengecek Relationship
Jika hanya perlu mengetahui apakah relationship memiliki data, gunakan withExists() pada versi Laravel yang mendukungnya.
Contoh:
$users = User::withExists('posts')->get();
Kemudian:
if ($user->posts_exists) {
// User memiliki post
}
Ini lebih efisien dibanding mengambil seluruh relationship hanya untuk melakukan pengecekan.
13. Gunakan Pagination
Jangan mengembalikan ribuan record sekaligus ke frontend.
Kurang ideal:
$posts = Post::latest()->get();
Lebih baik:
$posts = Post::latest()->paginate(20);
Pagination membatasi jumlah data yang diproses dan dikirim pada setiap request.
Untuk API, pagination juga membantu mengurangi ukuran response JSON.
14. Gunakan simplePaginate() Jika Tidak Membutuhkan Total Data
Jika aplikasi hanya membutuhkan tombol halaman berikutnya dan sebelumnya, simplePaginate() dapat menjadi pilihan:
$posts = Post::latest()->simplePaginate(20);
Perbedaan utamanya adalah pagination biasa perlu menghitung informasi total tertentu, sedangkan simplePaginate() menggunakan pendekatan yang lebih sederhana.
Untuk dataset yang besar, hal ini dapat membantu mengurangi pekerjaan database dalam situasi tertentu.
15. Gunakan cursorPaginate() untuk Dataset Besar
Untuk data yang sangat besar dan membutuhkan pagination yang efisien, pertimbangkan cursor pagination:
$posts = Post::orderBy('id')->cursorPaginate(20);
Cursor pagination cocok untuk skenario seperti:
- Feed.
- Timeline.
- Log aktivitas.
- Dataset besar.
- Infinite scrolling.
Cursor pagination memiliki karakteristik berbeda dari pagination berbasis nomor halaman. Karena itu, pilih metode berdasarkan kebutuhan navigasi frontend.
16. Gunakan chunk() untuk Data dalam Jumlah Besar
Jangan mengambil jutaan record sekaligus:
$users = User::all();
foreach ($users as $user) {
// Proses data
}
Gunakan chunk():
User::chunk(500, function ($users) {
foreach ($users as $user) {
// Proses data
}
});
Laravel akan mengambil data dalam beberapa kelompok sehingga penggunaan memory lebih terkendali.
17. Gunakan chunkById() untuk Pemrosesan yang Lebih Aman
Jika data diproses sambil mengalami perubahan, chunkById() sering kali lebih aman dibanding chunk().
Contoh:
User::chunkById(500, function ($users) {
foreach ($users as $user) {
// Proses data
}
});
Metode ini sangat berguna untuk pekerjaan batch pada dataset besar.
18. Gunakan lazy() atau lazyById()
Untuk pemrosesan data besar, Laravel juga menyediakan lazy collections.
Contoh:
foreach (User::lazy() as $user) {
// Proses satu per satu
}
Atau:
foreach (User::lazyById() as $user) {
// Proses data
}
Pendekatan ini dapat membantu menjaga penggunaan memory tetap rendah dibandingkan memuat seluruh dataset ke memory sekaligus.
19. Tambahkan Index pada Kolom yang Sering Dicari
Optimasi Eloquent tidak hanya dilakukan pada kode PHP. Struktur database juga sangat penting.
Misalnya aplikasi sering menjalankan:
User::where('email', $email)->first();
Kolom email sebaiknya memiliki index yang sesuai.
Migration:
$table->string('email')->unique();
Untuk kolom biasa:
$table->index('status');
Jika query sering menggunakan kombinasi kolom:
$table->index(['status', 'created_at']);
Index harus dirancang berdasarkan pola query yang benar-benar digunakan aplikasi.
20. Jangan Berlebihan Membuat Index
Index memang dapat mempercepat pencarian, tetapi bukan berarti semua kolom harus diberi index.
Terlalu banyak index dapat:
- Menggunakan ruang penyimpanan.
- Menambah pekerjaan saat
INSERT. - Menambah pekerjaan saat
UPDATE. - Membuat struktur database lebih kompleks.
Gunakan index berdasarkan query yang penting dan sering dijalankan.
21. Gunakan whereIn() daripada Banyak Query
Jika perlu mencari beberapa ID, hindari menjalankan query berulang:
foreach ($ids as $id) {
$user = User::find($id);
}
Lebih baik:
$users = User::whereIn('id', $ids)->get();
Database dapat mengambil seluruh data yang dibutuhkan dalam satu query.
22. Gunakan whereBetween() untuk Rentang Data
Untuk mengambil data dalam rentang tertentu:
$orders = Order::whereBetween('created_at', [
$startDate,
$endDate,
])->get();
Ini lebih sederhana daripada membuat banyak kondisi where.
23. Gunakan whereDate() dengan Pertimbangan Index
Laravel menyediakan:
Post::whereDate('created_at', today())->get();
Syntax tersebut mudah dibaca, tetapi untuk query yang sangat besar, Anda perlu memperhatikan bagaimana database menggunakan index pada kolom tanggal.
Untuk range waktu, sering kali pendekatan berikut lebih ramah terhadap index:
Post::where('created_at', '>=', $start)
->where('created_at', '<', $end)
->get();
Tetap lakukan pengujian menggunakan query plan database Anda karena performa bergantung pada database engine dan struktur index.
24. Gunakan whereExists() untuk Kondisi Relationship Kompleks
Untuk kebutuhan tertentu, whereExists() dapat lebih sesuai daripada mengambil relationship terlebih dahulu.
Contoh:
$users = User::whereExists(function ($query) {
$query->selectRaw('1')
->from('posts')
->whereColumn('posts.user_id', 'users.id');
})->get();
Query seperti ini berguna ketika kita hanya ingin mendapatkan user yang memiliki data terkait tanpa mengambil seluruh relationship.
25. Gunakan Query Builder Jika Tidak Membutuhkan Model Eloquent
Eloquent sangat nyaman, tetapi tidak setiap operasi membutuhkan object model.
Untuk query sederhana atau operasi tertentu, Query Builder dapat digunakan:
$users = DB::table('users')
->select('id', 'name')
->where('status', 'active')
->get();
Query Builder dapat menjadi pilihan ketika fitur Eloquent seperti accessor, mutator, event, atau relationship tidak dibutuhkan.
26. Hindari SELECT * untuk Query Berat
Query seperti:
SELECT * FROM users;
mengambil semua kolom.
Melalui Eloquent, Anda dapat mengontrol kolom:
User::select(['id', 'name', 'email'])->get();
Ini sangat penting jika tabel memiliki kolom besar atau data yang tidak dibutuhkan oleh operasi tersebut.
27. Gunakan toBase() untuk Kebutuhan Tertentu
Jika Anda tidak membutuhkan Eloquent model dan hanya membutuhkan hasil query dasar, toBase() dapat digunakan:
$users = User::query()
->select(['id', 'name'])
->where('status', 'active')
->toBase()
->get();
Dengan demikian, hasil tidak perlu diproses sebagai instance model Eloquent.
Gunakan pendekatan ini hanya ketika memang membutuhkan hasil query yang lebih sederhana.
28. Hindari Query di Dalam Loop
Kesalahan yang sering terjadi:
foreach ($posts as $post) {
$comments = Comment::where('post_id', $post->id)->get();
}
Query dijalankan berulang kali.
Gunakan relationship dan eager loading:
$posts = Post::with('comments')->get();
Kemudian:
foreach ($posts as $post) {
foreach ($post->comments as $comment) {
// Proses comment
}
}
29. Gunakan withWhereHas() Jika Memerlukan Filter dan Eager Loading
Jika aplikasi membutuhkan relationship yang memenuhi kondisi tertentu dan relationship tersebut juga perlu dimuat, withWhereHas() dapat membantu.
Contoh:
$posts = Post::withWhereHas('comments', function ($query) {
$query->where('approved', true);
})->get();
Dengan demikian, query dapat sekaligus memfilter model berdasarkan relationship dan memuat relationship yang relevan.
30. Perhatikan whereHas()
whereHas() sangat berguna untuk memfilter berdasarkan relationship:
$posts = Post::whereHas('comments', function ($query) {
$query->where('approved', true);
})->get();
Namun, jika Anda juga membutuhkan data relationship tersebut pada response, pertimbangkan eager loading agar tidak menimbulkan query tambahan.
31. Gunakan with() Secara Selektif
Eager loading memang penting, tetapi jangan memuat seluruh relationship tanpa kebutuhan.
Kurang ideal:
Post::with([
'user',
'comments',
'comments.user',
'tags',
'category',
])->get();
jika halaman hanya membutuhkan author.
Lebih baik:
Post::with('user')->get();
Prinsipnya sederhana: load data yang dibutuhkan, bukan semua data yang tersedia.
32. Gunakan Model::preventLazyLoading() Saat Development
Untuk membantu menemukan masalah N+1 query, Laravel menyediakan mekanisme untuk mencegah lazy loading ketika development.
Contoh pada konfigurasi aplikasi:
use Illuminate\Database\Eloquent\Model;
public function boot(): void
{
Model::preventLazyLoading(! $this->app->isProduction());
}
Ketika kode mencoba mengakses relationship yang belum di-load, developer dapat segera mengetahui bahwa ada kemungkinan lazy loading yang tidak disengaja.
Fitur ini sangat berguna untuk menjaga kualitas query selama pengembangan.
33. Gunakan withDefault() untuk Relationship Opsional
Jika relationship boleh kosong, withDefault() dapat menghindari pengecekan berulang:
public function user()
{
return $this->belongsTo(User::class)->withDefault([
'name' => 'Guest',
]);
}
Ini lebih berkaitan dengan kemudahan pengelolaan relationship daripada optimasi query langsung, tetapi dapat membantu membuat kode lebih sederhana.
34. Analisis Query dengan DB::listen()
Jika ingin mengetahui query yang dijalankan Laravel, Anda dapat menggunakan DB::listen() saat development.
Contoh:
use Illuminate\Support\Facades\DB;
DB::listen(function ($query) {
logger()->debug($query->sql, $query->bindings);
});
Fitur ini dapat membantu mengetahui:
- Query apa yang dijalankan.
- Binding yang digunakan.
- Query yang terlalu sering dipanggil.
- Potensi N+1 query.
Jangan membiarkan logging query secara berlebihan aktif di production karena dapat menghasilkan volume log yang besar.
35. Gunakan Query Log untuk Debugging
Laravel juga menyediakan query log untuk kebutuhan debugging tertentu:
DB::enableQueryLog();
$users = User::where('status', 'active')->get();
dd(DB::getQueryLog());
Gunakan fitur ini secara terbatas karena query log menyimpan informasi query selama proses berjalan.
36. Gunakan EXPLAIN untuk Query yang Lambat
Jika query database terasa lambat, jangan hanya menebak penyebabnya.
Periksa query plan menggunakan kemampuan database seperti EXPLAIN.
Misalnya query SQL:
EXPLAIN SELECT *
FROM posts
WHERE status = 'published'
ORDER BY created_at DESC;
Informasi dari query plan dapat membantu mengetahui apakah database menggunakan index atau melakukan scan yang mahal.
Untuk query production yang penting, analisis query plan lebih berguna daripada sekadar melihat syntax Eloquent.
37. Perhatikan Sorting dan Index
Query seperti:
Post::where('status', 'published')
->orderBy('created_at', 'desc')
->get();
dapat menjadi lebih efisien jika struktur index sesuai dengan pola query.
Contoh migration:
$table->index(['status', 'created_at']);
Namun, jangan langsung menambahkan composite index tanpa mengukur kebutuhan. Gunakan query plan dan karakteristik database untuk menentukan index yang tepat.
38. Gunakan Mass Update Jika Tidak Membutuhkan Event Model
Jika perlu memperbarui banyak record dengan kondisi yang sama:
User::where('status', 'inactive')
->update([
'status' => 'archived',
]);
Ini lebih efisien daripada:
$users = User::where('status', 'inactive')->get();
foreach ($users as $user) {
$user->status = 'archived';
$user->save();
}
Namun, perhatikan bahwa mass update tidak memicu event model saving, saved, updating, dan updated untuk setiap model secara individual.
Jika aplikasi bergantung pada event tersebut, pertimbangkan konsekuensinya sebelum menggunakan mass update.
39. Gunakan Mass Delete dengan Pertimbangan
Hal serupa berlaku pada delete:
User::where('status', 'deleted')->delete();
Operasi ini lebih efisien dibanding mengambil seluruh model kemudian menghapus satu per satu.
Namun, perhatikan behavior model events, observer, soft deletes, dan business rule yang mungkin terlibat.
40. Cache Query yang Mahal Jika Data Tidak Sering Berubah
Jika sebuah query mahal tetapi hasilnya jarang berubah, caching dapat membantu.
Contoh:
$categories = Cache::remember(
'categories',
now()->addHour(),
fn () => Category::orderBy('name')->get()
);
Dengan caching, aplikasi tidak harus menjalankan query yang sama setiap request.
Namun, cache harus memiliki strategi invalidation yang jelas agar data tidak menjadi terlalu lama.
41. Jangan Menggunakan Cache untuk Semua Query
Caching bukan solusi otomatis untuk semua masalah database.
Cache dapat menambah kompleksitas seperti:
- Cache invalidation.
- Data stale.
- Penggunaan memory.
- Sinkronisasi data.
Sebelum menambahkan cache, optimalkan query dan database terlebih dahulu.
42. Gunakan Soft Delete dengan Bijak
Jika model menggunakan SoftDeletes, query default akan mengecualikan data yang memiliki deleted_at.
Untuk tabel dengan jumlah data sangat besar, pertumbuhan record soft deleted perlu diperhatikan.
Dalam kondisi tertentu, data lama dapat membutuhkan strategi archival atau retention agar tabel utama tidak terus membesar tanpa kontrol.
43. Hindari Query Berulang untuk Data yang Sama
Misalnya:
$user = User::find($id);
// ...
$userAgain = User::find($id);
Jika data yang sama masih tersedia, hindari mengambilnya lagi.
Gunakan object yang sudah ada:
$user = User::find($id);
// Gunakan $user kembali
Pada aplikasi kompleks, caching di level aplikasi atau service tertentu juga dapat dipertimbangkan jika pola aksesnya memang berulang.
44. Gunakan value() untuk Mengambil Satu Nilai
Jika hanya membutuhkan satu kolom:
$email = User::where('id', $id)->value('email');
Tidak perlu mengambil seluruh model:
$user = User::find($id);
$email = $user->email;
value() dapat membuat intent query lebih jelas dan mengurangi data yang diambil.
45. Gunakan pluck() untuk Daftar Nilai Sederhana
Jika hanya membutuhkan daftar satu kolom:
$emails = User::where('status', 'active')
->pluck('email');
Daripada:
$users = User::where('status', 'active')->get();
foreach ($users as $user) {
$emails[] = $user->email;
}
pluck() lebih langsung karena database hanya mengembalikan kolom yang dibutuhkan.
46. Jangan Mengoptimalkan Tanpa Pengukuran
Salah satu prinsip penting dalam optimasi database adalah ukur sebelum dan sesudah perubahan.
Perhatikan:
- Waktu eksekusi query.
- Jumlah query.
- Penggunaan memory.
- Ukuran response.
- Query plan.
- Beban CPU database.
- Beban aplikasi.
Query yang terlihat sederhana belum tentu lambat. Sebaliknya, query yang terlihat rapi dapat menjadi masalah ketika dijalankan jutaan kali.
47. Gunakan Database Index Berdasarkan Pola Query
Misalnya aplikasi sering menjalankan:
Order::where('user_id', $userId)
->where('status', 'paid')
->latest()
->get();
Pertimbangkan index yang mendukung pola tersebut:
$table->index([
'user_id',
'status',
'created_at',
]);
Tetapi index terbaik bergantung pada database engine, cardinality data, urutan kondisi, dan query lain yang berjalan pada tabel tersebut.
Karena itu, gunakan EXPLAIN atau tool profiling database untuk memvalidasi keputusan.
48. Contoh Optimasi Query Sebelum dan Sesudah
Misalnya kita memiliki kode:
$posts = Post::all();
foreach ($posts as $post) {
echo $post->user->name;
}
Ada dua masalah:
- Mengambil seluruh post.
- Berpotensi menimbulkan N+1 query.
Kita dapat memperbaikinya:
$posts = Post::with('user')
->select(['id', 'user_id', 'title'])
->latest()
->paginate(20);
Sekarang:
- Data dibatasi dengan pagination.
- Relationship user menggunakan eager loading.
- Kolom yang tidak diperlukan tidak diambil.
- Foreign key
user_idtetap tersedia. - Query lebih sesuai untuk endpoint API.
49. Checklist Optimasi Laravel Eloquent
Sebelum menyatakan query sudah optimal, periksa beberapa hal berikut:
- Apakah aplikasi mengambil semua kolom padahal hanya membutuhkan beberapa?
- Apakah
all()benar-benar diperlukan? - Apakah terdapat query di dalam loop?
- Apakah relationship sudah menggunakan eager loading?
- Apakah ada N+1 query?
- Apakah pagination sudah digunakan?
- Apakah dataset besar diproses menggunakan
chunk()atau lazy collection? - Apakah kolom pencarian memiliki index yang sesuai?
- Apakah query agregasi menggunakan
count(),sum(), atau fungsi database lain? - Apakah query yang lambat sudah diperiksa menggunakan
EXPLAIN? - Apakah caching memang diperlukan?
- Apakah jumlah query sudah diukur sebelum dan sesudah optimasi?
Kesimpulan
Laravel Eloquent memberikan abstraction yang sangat nyaman untuk bekerja dengan database, tetapi developer tetap perlu memahami bagaimana query tersebut dieksekusi.
Optimasi yang paling sering memberikan dampak adalah menghindari pengambilan data berlebihan, menggunakan select(), eager loading untuk mencegah N+1 query, pagination, agregasi database, chunk() untuk dataset besar, serta index yang sesuai dengan pola query.
Untuk masalah yang lebih kompleks, gunakan query profiling dan EXPLAIN untuk mengetahui apa yang sebenarnya dilakukan database. Jangan hanya mengandalkan perkiraan berdasarkan syntax Eloquent.
Pada akhirnya, aplikasi Laravel yang cepat bukan sekadar menggunakan syntax Eloquent yang singkat. Performa ditentukan oleh kombinasi antara query yang efisien, desain database, index, jumlah data, arsitektur aplikasi, dan cara aplikasi mengakses database.
FAQ
Apakah Eloquent lebih lambat daripada Query Builder?
Eloquent memiliki overhead karena bekerja dengan model dan fitur ORM seperti relationship, accessor, event, dan casting. Namun, perbedaannya tidak selalu menjadi masalah. Query Builder dapat digunakan ketika aplikasi tidak membutuhkan fitur Eloquent dan ingin bekerja dengan hasil query yang lebih sederhana.
Apa itu N+1 query pada Laravel?
N+1 terjadi ketika aplikasi menjalankan satu query untuk mengambil data utama, kemudian menjalankan query tambahan berulang kali untuk setiap item ketika mengakses relationship. Eager loading dengan with() merupakan salah satu cara utama untuk mengatasinya.
Apakah select() benar-benar membuat Laravel lebih cepat?
Mengambil hanya kolom yang diperlukan dapat mengurangi jumlah data yang dikirim dari database dan diproses aplikasi. Dampaknya semakin terasa pada tabel dengan banyak kolom atau data berukuran besar.
Kapan harus menggunakan chunk()?
Gunakan chunk() ketika perlu memproses dataset besar dan tidak ingin memuat seluruh record ke memory sekaligus.
Apa perbedaan paginate() dan cursorPaginate()?
paginate() menggunakan pagination berbasis halaman dan menyediakan informasi jumlah halaman. cursorPaginate() menggunakan cursor dan cocok untuk dataset besar, feed, atau infinite scrolling ketika navigasi berdasarkan nomor halaman tidak menjadi kebutuhan utama.
Apakah semua kolom database perlu diberi index?
Tidak. Index sebaiknya diberikan pada kolom yang memang sering digunakan dalam pencarian, filtering, join, sorting, atau constraint tertentu. Terlalu banyak index juga dapat meningkatkan biaya operasi write.
Bagaimana cara mengetahui query Laravel yang lambat?
Gunakan query logging atau profiling selama development, kemudian analisis SQL menggunakan EXPLAIN pada database. Untuk aplikasi production, gunakan monitoring dan APM yang sesuai agar masalah performa dapat diidentifikasi tanpa logging berlebihan.
Apakah caching dapat menggantikan optimasi query?
Tidak. Caching merupakan lapisan tambahan. Query dan struktur database tetap perlu dioptimalkan terlebih dahulu agar aplikasi tidak bergantung sepenuhnya pada cache.

