Aplikasi terasa instan di laptop dengan data seed 50 baris, lalu tercekik saat tabel produksi menyentuh puluhan ribu. Biang keroknya sering satu pola sederhana: N+1 query. Ia lolos review karena kodenya terlihat wajar — dan baru terasa saat n bertumbuh.
Apa yang sebenarnya terjadi
Pola N+1 muncul saat Anda mengambil daftar (1 query), lalu untuk setiap item mengambil data terkait dengan satu query lagi (N query). Sepuluh item = 11 query; sepuluh ribu item = 10.001 query. Di data kecil tak terasa; di data nyata, ia meledak secara linier.
Yang membuatnya berbahaya: kodenya sering ditulis dalam loop yang terlihat bersih, atau tersembunyi di balik lazy-loading ORM. Tidak ada yang 'salah' secara sintaksis — yang salah adalah jumlah round-trip ke database.
Perbaikannya: batasi round-trip, bukan mempercantik loop
- Ambil data terkait dalam satu query dengan JOIN, atau satu query IN untuk semua id sekaligus (dua query total, bukan N+1).
- Untuk hubungan banyak-ke-banyak, ambil batch lalu petakan di memori (O(n)) alih-alih query per item.
- Pastikan kolom yang difilter/di-join punya index yang tepat — komposit yang sesuai urutan query.
- Ukur dengan menghitung query aktual (log/tracing), bukan menebak. Angka query per request adalah metrik yang jujur.
Uji di ukuran n yang realistis
Akar masalahnya sering bukan kodenya, tapi datanya: tes dan demo memakai n yang terlalu kecil untuk memunculkan masalah. Uji jalur data-berat pada ukuran yang mendekati produksi, dan regresi performa akan tertangkap saat masih murah diperbaiki.
Aturan praktis: sebelum menulis loop yang menyentuh database, tanyakan berapa query yang akan dihasilkan saat n besar. Kalau jawabannya tumbuh bersama n, ada yang perlu dibatch.
Ringkasan
N+1 bukan bug eksotis, tapi pola default yang mudah ditulis tanpa sadar. Kenali bentuknya, batasi round-trip dengan JOIN/IN + index yang tepat, dan uji di n realistis. Perbedaan antara instan dan timeout sering hanya soal jumlah query.