Episode ini membahas cara menganalisis dan mengoptimasi query: membaca execution plan dengan EXPLAIN dan EXPLAIN ANALYZE, memahami Sequential Scan Index Scan dan Bitmap, join algorithms Nested Loop Hash dan Merge Join, serta identifikasi query lambat dengan pg_stat_statements.

Selamat datang di episode 15 series Belajar SQL PostgreSQL! Pada episode 14 kita belajar membuat index — tapi bagaimana kita tahu index itu benar-benar terpakai? Bagaimana caranya menemukan mengapa sebuah query lambat? Jawabannya bukan menebak, melainkan membaca execution plan — cetak biru cara PostgreSQL menjalankan query kalian. Keterampilan ini adalah "superpower" seorang database developer.
Ketika ada query yang lambat, jangan langsung menebak-nebak solusi. Jalankan EXPLAIN ANALYZE, baca dengan teliti, dan biarkan data berbicara. Apakah query-nya melakukan full table scan padahal seharusnya pakai index? Apakah join-nya memilih algoritma yang salah? Semua jawaban ada di execution plan.
Di episode ini, kita akan membahas perintah EXPLAIN dan EXPLAIN ANALYZE, memahami komponen execution plan seperti Sequential Scan, Index Scan, dan Bitmap Index Scan, tiga join algorithms (Nested Loop, Hash Join, Merge Join), cara membaca metrik cost, rows, width, actual time, dan loops, serta mengidentifikasi query lambat di produksi dengan ekstensi pg_stat_statements.
EXPLAIN menampilkan rencana eksekusi yang diperkirakan planner — tanpa benar-benar menjalankan query. EXPLAIN ANALYZE menjalankan query sungguhan dan menampilkan statistik aktual. Untuk analisis performa nyata, selalu gunakan EXPLAIN ANALYZE.
EXPLAIN ANALYZE
SELECT * FROM users WHERE email = 'budi@example.com';Index Scan using idx_users_email on users (cost=0.29..8.31 rows=1 width=44)
Index Cond: (email = 'budi@example.com'::text)
Actual Time: 0.032..0.034 rows=1 loops=1
Planning Time: 0.143 ms
Execution Time: 0.045 msBaca dari dalam ke luar: PostgreSQL memakai Index Scan dengan kondisi Index Cond. Waktu eksekusinya 0.045 ms — sangat cepat. Jika output menunjukkan Seq Scan di tabel besar dengan Actual Time ratusan milidetik, itu sinyal ada index yang hilang.
Tip
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) adalah versi "lengkap" yang menampilkan detail I/O per node — sangat membantu menemukan query yang banyak membaca disk. Untuk query yang menulis data, bungkus dengan transaksi: BEGIN; EXPLAIN ANALYZE UPDATE ...; ROLLBACK; agar perubahan tidak benar-benar tersimpan.
Tiga cara PostgreSQL mengakses tabel:
EXPLAIN ANALYZE SELECT * FROM orders WHERE total > 500000;Jika baris yang cocok sangat banyak, planner mungkin memilih Seq Scan karena lebih murah daripada memakai index satu-per-satu. Ini bukan berarti indexnya jelek — planner tahu kapan index tidak sepadan.
Tiga algoritma join yang dipilih planner:
| Algoritma | Cara kerja | Cocok untuk |
|---|---|---|
| Nested Loop | Untuk tiap baris luar, cari kecocokan di dalam (dengan index) | Dataset kecil atau join selektif dengan index |
| Hash Join | Bangun hash table dari tabel kecil, cocokkan baris besar | Join dua dataset besar tanpa kondisi index |
| Merge Join | Gabungkan dua hasil yang sudah terurut | Join yang cocok dengan kondisi ORDER BY |
EXPLAIN ANALYZE
SELECT c.full_name, o.total
FROM customers c
JOIN orders o ON o.customer_id = c.id;Output akan menunjukkan Hash Join atau Nested Loop — dan dari situ kalian bisa menilai apakah index orders(customer_id) perlu dibuat (Nested Loop sangat diuntungkan index pada sisi dalam).
Setiap node menampilkan metrik:
start..total). Satuan abstrak — bandingkan antar rencana, bukan nilai absolut.start..end per loop, dalam milidetik.loops tinggi pada inner scan adalah penanda Nested Loop yang mahal.Warning
Tanda paling berbahaya di execution plan: estimasi jauh meleset dari aktual (misal rows=10 tapi actual rows=1.000.000). Ini berarti statistik tabel kedaluwarsa — jalankan ANALYZE users; untuk memperbarui statistik, atau pertimbangkan meningkatkan default_statistics_target untuk kolom tersebut.
Tidak semua query lambat bisa direproduksi di lokal. Di produksi dengan beban nyata, kita butuh alat untuk menemukan query yang paling banyak menyita waktu. Ekstensi pg_stat_statements adalah jawabannya.
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;Agar ekstensi ini berfungsi penuh, parameter shared_preload_libraries = 'pg_stat_statements' harus diset di postgresql.conf lalu server di-restart. Setelah itu, ekstensi mulai mencatat semua query yang dieksekusi.
SELECT
query,
calls,
total_exec_time / 1000 AS total_ms,
mean_exec_time / 1000 AS mean_ms,
rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;Hasilnya menampilkan query dengan total waktu eksekusi terbesar — kandidat utama optimasi. Perhatikan kolom calls (frekuensi) dan mean_exec_time (rata-rata): query yang dipanggil ribuan kali dengan rata-rata tinggi adalah masalah terbesar.
Note
pg_stat_statements menormalkan parameter query (nilai literal diganti placeholder), sehingga query yang sama dengan parameter berbeda dikumpulkan dalam satu baris. Untuk me-reset statistik: SELECT pg_stat_statements_reset();. Data reset berguna sebelum melakukan A/B testing optimasi.
pg_stat_statements atau log slow query (log_min_duration_statement di postgresql.conf).EXPLAIN (ANALYZE, BUFFERS) pada query tersebut.Seq Scan pada tabel besar? Apakah rows meleset dari aktual?Execution Time sebelum dan sesudah.| # | Kesalahan | Gejala | Solusi |
|---|---|---|---|
| 1 | Pakai EXPLAIN saja | Estimasi bisa menyesatkan | Gunakan EXPLAIN ANALYZE |
| 2 | Statistik kedaluwarsa | rows estimasi jauh dari aktual | Jalankan ANALYZE |
| 3 | Mengabaikan loops tinggi | Nested Loop tersembunyi sangat mahal | Cek index pada sisi dalam |
| 4 | Optimasi tanpa pengukuran | Solusi "tebakan" yang tidak terverifikasi | Bandingkan waktu sebelum/sesudah |
Di episode 15 ini kita sudah menguasai optimasi query berbasis bukti: membaca execution plan dengan EXPLAIN dan EXPLAIN ANALYZE, memahami perbedaan Sequential Scan, Index Scan, dan Bitmap Index Scan, tiga join algorithms (Nested Loop, Hash Join, Merge Join), metrik cost, rows, width, actual time, dan loops, serta menemukan query lambat di produksi dengan pg_stat_statements.
Inti yang harus dibawa pulang:
EXPLAIN ANALYZE menjalankan query sungguhan — selalu pakai untuk analisis aktual.Seq Scan di tabel besar = sinyal index hilang; Bitmap Scan = banyak baris cocok tersebar.rows estimasi vs aktual yang meleset menunjukkan statistik basi → ANALYZE.pg_stat_statements adalah kamera pengawas query di produksi.Di episode 16 selanjutnya, kita masuk ke keamanan: Roles, Security Management & Row Level Security (RLS) — mulai dari konsep role yang menggabungkan user dan group, perintah CREATE ROLE, GRANT, dan REVOKE, hingga RLS untuk keamanan multi-tenant di tingkat baris, serta pengamanan pg_hba.conf.