Saat tim makin bergantung pada generator query, ORM, atau AI untuk menulis SQL, satu hal sering ikut hilang: intuisi tentang bagaimana database benar-benar mengeksekusi query. Akibatnya, query yang tampak benar secara fungsional tetap lambat, boros I/O, dan baru terasa saat data produksi membesar.
Audit query lambat adalah cara paling praktis untuk mengembalikan kontrol. Fokusnya bukan menebak-nebak optimasi, melainkan membaca gejala nyata, memeriksa rencana eksekusi, lalu memutuskan apakah perbaikannya harus dilakukan di level query, index, atau desain akses data. Jika pengetahuan indexing di tim mulai melemah, inilah area yang harus dipulihkan lebih dulu.
Belakangan banyak tim menyadari bahwa otomasi tidak otomatis menjaga expertise. Ketika AI bisa membantu menulis potongan SQL tetapi tidak memahami pola data, kardinalitas, dan beban produksi spesifik aplikasi Anda, organisasi tetap perlu engineer yang bisa mengaudit query secara manual. Artikel ini fokus pada praktik tersebut.
Tanda bottleneck nyata: jangan mulai dari SQL, mulai dari gejala
Query lambat jarang pertama kali terlihat dari kode. Biasanya ia muncul sebagai gejala sistem:
- Lonjakan latency endpoint tertentu, terutama pada halaman daftar, laporan, atau pencarian.
- CPU database naik tanpa kenaikan trafik yang sebanding.
- I/O disk tinggi karena banyak pembacaan data yang seharusnya bisa dipersempit oleh index.
- Connection pool habis karena query lama menahan koneksi terlalu lama.
- Timeout pada worker, API, atau job batch.
- Performa memburuk seiring pertumbuhan data, walau kode aplikasi tidak berubah.
Gejala ini penting karena membantu membedakan query lambat dari masalah lain seperti lock, bottleneck jaringan, konfigurasi pool, atau N+1 query di aplikasi.
Langkah awal investigasi
- Ambil contoh endpoint atau job yang lambat.
- Catat query yang benar-benar dijalankan, bukan hanya query yang diperkirakan ORM.
- Ukur durasi, frekuensi, dan parameter yang umum dipakai.
- Jalankan
EXPLAINatauEXPLAIN ANALYZEuntuk melihat rencana eksekusi. - Periksa apakah masalah dominan ada pada scanning, sorting, joining, filtering, atau pagination.
Catatan: Query yang cepat pada data kecil bisa menjadi sangat lambat saat tabel tumbuh. Audit yang baik selalu mempertimbangkan ukuran data nyata dan distribusinya, bukan hanya hasil di lingkungan lokal.
Cara audit query lambat dengan EXPLAIN dan EXPLAIN ANALYZE
Tujuan utama EXPLAIN adalah menjawab pertanyaan sederhana: database melakukan pekerjaan apa untuk menjawab query ini? Bukan semua engine menampilkan output yang sama, tetapi prinsip membacanya serupa.
Apa yang perlu dicari
- Jenis scan: apakah database memakai index scan atau full table scan.
- Jumlah baris yang diperkirakan dan yang benar-benar diproses.
- Urutan join: tabel mana yang dibaca dulu dan bagaimana relasinya dievaluasi.
- Sorting tambahan: apakah
ORDER BYmemaksa proses sort mahal. - Filter yang terlambat diterapkan: data dibaca banyak dulu, baru dibuang belakangan.
Secara umum, EXPLAIN menunjukkan rencana. EXPLAIN ANALYZE biasanya menambahkan eksekusi nyata dan waktu aktual. Untuk audit performa, hasil aktual lebih berguna karena menunjukkan apakah estimasi planner meleset jauh.
Contoh query yang tampak sederhana, tetapi bermasalah
SELECT id, user_id, status, created_at
FROM orders
WHERE status = 'paid'
ORDER BY created_at DESC
LIMIT 50;Query ini umum pada halaman admin atau dashboard. Masalahnya: tanpa index yang tepat, database bisa saja membaca banyak baris dengan status = 'paid', lalu menyortir hasilnya sebelum mengambil 50 teratas.
Hal yang ingin Anda lihat dari hasil EXPLAIN:
- Apakah filter
status = 'paid'memakai index. - Apakah
ORDER BY created_at DESCbisa dipenuhi dari urutan index, bukan sort terpisah. - Berapa banyak baris yang tetap harus diperiksa sebelum menemukan 50 hasil.
Jika planner menunjukkan full scan atau sort besar, itu tanda bahwa query atau index belum sesuai dengan pola akses data.
Kapan EXPLAIN tidak cukup
Ada kasus ketika query tampak baik di EXPLAIN, tetapi tetap lambat di produksi. Penyebab umum:
- Parameter query di produksi berbeda dari pengujian lokal.
- Statistik tabel tidak akurat sehingga planner salah memilih rencana.
- Kontensi lock atau transaksi panjang.
- Index ada, tetapi selektivitasnya rendah.
- Data sudah terlalu besar untuk pola akses yang dipakai.
Karena itu, audit query lambat harus selalu dikaitkan dengan beban dan pola data nyata.
Menemukan full scan dan memahami kenapa itu terjadi
Full scan berarti database membaca seluruh atau sebagian besar tabel karena tidak ada cara yang lebih sempit untuk menemukan data yang dibutuhkan. Full scan tidak selalu salah, tetapi sering menjadi masalah pada tabel besar dengan query yang seharusnya spesifik.
Penyebab full scan yang paling umum
- Tidak ada index pada kolom filter atau join.
- Index ada, tetapi urutannya tidak cocok untuk query.
- Query memakai fungsi pada kolom yang diindeks, sehingga index sulit dipakai.
- Pencarian terlalu tidak selektif, sehingga planner memilih scan penuh.
- Kondisi
ORatau ekspresi kompleks membuat index kurang efektif.
Contoh yang sering merusak index
SELECT *
FROM users
WHERE DATE(created_at) = '2025-01-15';Secara logika benar, tetapi memanggil fungsi pada kolom dapat menghambat pemakaian index biasa pada created_at. Bentuk yang umumnya lebih ramah index:
SELECT *
FROM users
WHERE created_at >= '2025-01-15 00:00:00'
AND created_at < '2025-01-16 00:00:00';Mengapa ini bekerja? Karena database bisa membandingkan rentang nilai langsung terhadap urutan index, tanpa menghitung fungsi untuk setiap baris.
Contoh gejala produksi yang mengarah ke full scan
- Query daftar data tiba-tiba lambat setelah tabel menembus jutaan baris.
- Endpoint pencarian internal hanya lambat untuk kata kunci kosong atau terlalu umum.
- Job harian yang dulu selesai cepat sekarang memakan waktu jauh lebih lama walau logika tidak berubah.
Memilih index yang tepat, bukan sekadar menambah index
Kesalahan umum saat pengetahuan SQL melemah adalah menganggap setiap query lambat bisa diperbaiki dengan “tambahkan index”. Pendekatan ini sering membantu sesaat, tetapi mudah berujung ke skema yang sulit dirawat.
Prinsip dasar memilih index
Index harus mengikuti pola akses data yang paling penting. Perhatikan tiga hal:
- Kolom filter pada
WHERE. - Kolom join pada relasi antartabel.
- Kolom urutan pada
ORDER BY.
Untuk query berikut:
SELECT id, user_id, status, created_at
FROM orders
WHERE user_id = 42 AND status = 'paid'
ORDER BY created_at DESC
LIMIT 20;Index tunggal pada user_id, status, dan created_at secara terpisah belum tentu optimal. Sering kali query seperti ini membutuhkan composite index yang mengikuti pola filter dan urutan baca.
Mengapa composite index bisa lebih baik? Karena database tidak hanya butuh menemukan baris yang cocok, tetapi juga idealnya membacanya dalam urutan yang sudah sesuai, sehingga mengurangi sort tambahan.
Trade-off dalam pemilihan index
- Lebih cepat untuk read, tetapi lebih mahal untuk write karena setiap insert/update/delete harus memperbarui index terkait.
- Index besar memakan storage dan memori lebih banyak.
- Terlalu banyak index memperlambat proses maintenance dan bisa membingungkan planner.
Bahaya over-indexing
Over-indexing terjadi saat tabel memiliki terlalu banyak index yang saling tumpang tindih atau jarang dipakai. Ini sering muncul ketika tiap query lambat diberi index baru tanpa evaluasi keseluruhan.
Gejalanya:
- Write throughput turun.
- Ukuran tabel dan storage membesar lebih cepat dari yang diperkirakan.
- Index yang mirip-mirip dibuat untuk kasus yang sebenarnya bisa dilayani satu desain index yang lebih baik.
Sebelum menambah index, tanyakan:
- Apakah query ini cukup sering dan cukup penting?
- Apakah sudah ada index yang sebenarnya hampir cocok?
- Apakah masalah utamanya benar di index, atau query mengembalikan data terlalu banyak?
Kapan masalah harus diperbaiki di query, index, atau desain akses data
Tidak semua query lambat adalah masalah index. Berikut cara membedakannya.
Perbaiki di level query jika:
- Query mengambil kolom atau baris lebih banyak dari yang diperlukan.
- Filter ditulis dengan cara yang membuat index sulit digunakan.
- Terdapat subquery, join, atau sort yang sebenarnya bisa disederhanakan.
- Pola pagination membuat database terus membuang banyak baris.
Contoh perbaikan sederhana:
-- Kurang baik
SELECT *
FROM orders
WHERE user_id = 42
ORDER BY created_at DESC
LIMIT 50;-- Lebih baik jika hanya butuh beberapa kolom
SELECT id, status, total_amount, created_at
FROM orders
WHERE user_id = 42
ORDER BY created_at DESC
LIMIT 50;Mengambil kolom seperlunya mengurangi data yang dibaca dan dikirim, terutama jika tabel memiliki kolom besar seperti JSON atau teks panjang.
Perbaiki di level index jika:
- Query memang sudah spesifik, tetapi planner tetap melakukan scan besar.
- Kolom filter/join/order adalah pola akses yang stabil dan sering dipakai.
- Output
EXPLAINmenunjukkan bahwa struktur pencarian belum efisien.
Perbaiki di level desain akses data jika:
- Query harus membaca terlalu banyak data untuk kebutuhan antarmuka sederhana.
- Pagination tradisional tidak lagi masuk akal pada dataset besar.
- Laporan kompleks dipaksa berjalan di jalur request sinkron.
- Satu endpoint mencoba melayani terlalu banyak kebutuhan sekaligus.
Contoh desain akses data yang perlu dipikirkan ulang:
- Memisahkan query transaksi harian dari query analitik.
- Membuat ringkasan teragregasi untuk dashboard.
- Memindahkan laporan berat ke job asynchronous.
- Membatasi kebebasan filter pada UI agar query tetap dapat dioptimasi.
Offset vs keyset pagination: masalah klasik yang sering diabaikan
Pagination berbasis OFFSET mudah dipakai, tetapi bisa memburuk drastis saat halaman makin jauh. Alasannya sederhana: database sering tetap harus melewati banyak baris sebelum sampai ke baris yang ingin dikembalikan.
Contoh offset pagination
SELECT id, created_at, total_amount
FROM orders
WHERE status = 'paid'
ORDER BY created_at DESC
LIMIT 50 OFFSET 50000;Walau hasil yang dikembalikan hanya 50 baris, database bisa perlu memproses jauh lebih banyak untuk mencapai offset tersebut.
Alternatif: keyset pagination
SELECT id, created_at, total_amount
FROM orders
WHERE status = 'paid'
AND created_at < '2025-01-15 10:30:00'
ORDER BY created_at DESC
LIMIT 50;Pada praktiknya, keyset pagination memakai nilai penanda dari halaman sebelumnya, seperti created_at atau pasangan kolom yang menjamin urutan stabil. Mengapa lebih efisien? Karena database dapat melanjutkan pencarian dari posisi tertentu dalam index, bukan membuang puluhan ribu baris terlebih dahulu.
Kapan tetap memakai offset
- Dataset kecil.
- Kebutuhan navigasi langsung ke halaman tertentu lebih penting daripada efisiensi maksimal.
- UI internal dengan volume rendah.
Namun untuk feed, riwayat transaksi, log aktivitas, atau daftar besar yang sering dibuka, keyset biasanya lebih aman untuk jangka panjang.
Dampak pertumbuhan data: query yang tadinya aman bisa menjadi masalah
Salah satu penyebab audit query sering terlambat dilakukan adalah karena semua tampak normal pada awal proyek. Saat tabel masih ribuan baris, query buruk sering tidak terasa. Masalah baru muncul ketika volume naik, distribusi data berubah, atau pola akses bergeser.
Yang perlu diperhatikan saat data tumbuh
- Kardinalitas kolom: index pada kolom dengan variasi nilai sangat rendah tidak selalu membantu.
- Hot path: query yang dipanggil ratusan kali per detik harus diperlakukan berbeda dari query admin mingguan.
- Pertumbuhan historis: tabel event, audit log, dan transaksi biasanya membesar terus.
- Pola akses baru: fitur pencarian, filter tambahan, dan sort baru bisa mengubah kebutuhan index.
Karena itu, audit query lambat sebaiknya dilakukan berkala pada query yang paling sering dan paling mahal, bukan hanya saat sistem sudah mengalami insiden.
Checklist review query sebelum rilis
Jika pengetahuan SQL tim mulai memudar, checklist sederhana jauh lebih berguna daripada mengandalkan intuisi yang tidak lagi merata.
- Apakah query ini berjalan di jalur panas? Endpoint utama, login, checkout, feed, dan job penting wajib diperiksa.
- Apakah filter utamanya punya dukungan index yang masuk akal?
- Apakah urutan
ORDER BYselaras dengan pola index? - Apakah query mengambil kolom seperlunya, bukan
SELECT *tanpa alasan? - Apakah pagination memakai offset besar? Jika ya, evaluasi keyset.
- Apakah ada fungsi pada kolom filter yang menghambat index?
- Apakah join dilakukan pada kolom yang benar dan terindeks?
- Apakah hasil
EXPLAINmenunjukkan scan atau sort besar? - Apakah query ini masih aman jika volume data naik 10x?
- Apakah ada cara memindahkan beban ke cache, pre-aggregation, atau job async tanpa merusak konsistensi?
Alur investigasi praktis yang bisa dipakai tim
Untuk mengurangi ketergantungan pada tebakan, gunakan alur kerja tetap saat menemukan query lambat:
- Identifikasi query nyata dari log, APM, slow query log, atau instrumentation aplikasi.
- Kelompokkan berdasarkan dampak: frekuensi tinggi, durasi tinggi, atau memblokir proses penting.
- Jalankan EXPLAIN/EXPLAIN ANALYZE pada parameter yang representatif.
- Tentukan akar masalah dominan: scan, sort, join, atau pagination.
- Pilih perbaikan paling sempit: ubah query, tambah/perbaiki index, atau ubah desain akses data.
- Uji ulang dengan data yang cukup mirip produksi.
- Pantau setelah rilis untuk memastikan perbaikan benar-benar menurunkan beban.
Poin pentingnya: jangan langsung menambah index tanpa memahami mengapa query lambat. Query bisa saja lambat karena mengakses data terlalu banyak untuk kebutuhan yang sebenarnya kecil.
Kesalahan umum saat tim terlalu bergantung pada otomasi atau AI
- Menerima query “yang jalan” sebagai query “yang baik”.
- Mengabaikan rencana eksekusi karena SQL tampak sederhana di level kode.
- Menambah index satu per satu tanpa strategi.
- Terlalu percaya pada hasil lokal dengan dataset kecil.
- Tidak mendokumentasikan pola query penting, sehingga pengetahuan hilang saat engineer berganti.
AI bisa membantu mempercepat penulisan, memberi hipotesis, atau menjelaskan konsep. Tetapi keputusan performa tetap harus berpijak pada gejala produksi, rencana eksekusi, dan pemahaman terhadap data Anda sendiri.
Penutup
Audit query lambat bukan pekerjaan yang glamour, tetapi inilah salah satu kemampuan SQL yang paling penting ketika pengetahuan dasar tim mulai menurun. Dengan membaca gejala nyata, memeriksa EXPLAIN, mengenali full scan, memilih index secara sengaja, menghindari over-indexing, dan mengganti offset pagination saat perlu, Anda bisa memperbaiki performa secara sistematis.
Jika harus memilih satu kebiasaan untuk dikembalikan ke tim, pilih ini: setiap query penting harus bisa dijelaskan rencana eksekusinya. Dari situ, diskusi tentang apakah masalah ada di query, index, atau desain akses data akan menjadi jauh lebih objektif.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!