commit 007407631f70db0999f466fd5e46b9e280e32cc3 Author: mbahsomo Date: Wed Dec 31 13:31:17 2025 +0000 Add materialized_view_postgresql.MD diff --git a/materialized_view_postgresql.MD b/materialized_view_postgresql.MD new file mode 100644 index 0000000..99e3eca --- /dev/null +++ b/materialized_view_postgresql.MD @@ -0,0 +1,203 @@ +Berikut **strategi performa Materialized View di PostgreSQL (fokus INDEX & refresh)** yang sudah terbukti efektif untuk kasus laporan & query berat. + +--- + +## 1️⃣ **WAJIB: Buat INDEX di Materialized View** + +Materialized view **TIDAK otomatis punya index**. + +### Contoh + +```sql +CREATE MATERIALIZED VIEW mv_laporan_penjualan AS +SELECT + tanggal, + cabang_id, + SUM(total) AS total_penjualan +FROM penjualan +GROUP BY tanggal, cabang_id; +``` + +### Index untuk filter & join + +```sql +CREATE INDEX idx_mv_penjualan_tanggal +ON mv_laporan_penjualan (tanggal); + +CREATE INDEX idx_mv_penjualan_cabang +ON mv_laporan_penjualan (cabang_id); +``` + +πŸ“Œ **Rule utama** + +* `WHERE` β†’ index kolom filter +* `JOIN` β†’ index kolom join +* `ORDER BY` β†’ index kolom sorting + +--- + +## 2️⃣ **UNIQUE INDEX untuk REFRESH CONCURRENTLY** + +Tanpa ini, `CONCURRENTLY` **akan error**. + +```sql +CREATE UNIQUE INDEX ux_mv_penjualan +ON mv_laporan_penjualan (tanggal, cabang_id); +``` + +Lalu refresh: + +```sql +REFRESH MATERIALIZED VIEW CONCURRENTLY mv_laporan_penjualan; +``` + +🎯 **Benefit** + +* Query tetap bisa baca data +* Tidak lock materialized view + +--- + +## 3️⃣ **Gunakan Composite Index (bukan banyak index kecil)** + +❌ Buruk: + +```sql +(tanggal) +(cabang_id) +``` + +βœ… Lebih optimal: + +```sql +CREATE INDEX idx_mv_penjualan_tanggal_cabang +ON mv_laporan_penjualan (tanggal, cabang_id); +``` + +Urutan kolom **HARUS mengikuti pola query**: + +```sql +WHERE tanggal = ? +AND cabang_id = ? +``` + +--- + +## 4️⃣ **Partial Index untuk Data Populer** + +Kalau query sering ambil data terbaru: + +```sql +CREATE INDEX idx_mv_penjualan_30hari +ON mv_laporan_penjualan (tanggal) +WHERE tanggal >= CURRENT_DATE - INTERVAL '30 days'; +``` + +⚑ Sangat cepat untuk dashboard β€œbulan ini”. + +--- + +## 5️⃣ **Covering Index (INCLUDE)** + +Agar **Index Only Scan** terjadi: + +```sql +CREATE INDEX idx_mv_cover +ON mv_laporan_penjualan (tanggal, cabang_id) +INCLUDE (total_penjualan); +``` + +πŸ“ˆ Mengurangi akses ke heap β†’ performa naik. + +--- + +## 6️⃣ **VACUUM & ANALYZE setelah REFRESH** + +Materialized view **tidak auto-analyze**. + +```sql +ANALYZE mv_laporan_penjualan; +``` + +Untuk rutin: + +```sql +VACUUM ANALYZE mv_laporan_penjualan; +``` + +πŸ“Œ Sangat penting untuk planner memilih index yang benar. + +--- + +## 7️⃣ **Pisahkan MV untuk Use Case Berbeda** + +❌ Satu MV dipakai semua kebutuhan +βœ… Beberapa MV kecil & spesifik + +Contoh: + +```sql +mv_penjualan_harian +mv_penjualan_bulanan +mv_penjualan_per_cabang +``` + +Lebih cepat refresh & index lebih fokus. + +--- + +## 8️⃣ **Refresh Incremental (Strategi Lanjutan)** + +PostgreSQL **tidak native incremental refresh**. + +Solusi umum: + +* MV hanya data lama +* Data hari ini query langsung tabel + +```sql +SELECT * FROM mv_penjualan_lama +UNION ALL +SELECT ... FROM penjualan WHERE tanggal = CURRENT_DATE; +``` + +⚑ Refresh MV cukup seminggu sekali. + +--- + +## 9️⃣ **Gunakan EXPLAIN ANALYZE** + +Pastikan index benar-benar dipakai. + +```sql +EXPLAIN ANALYZE +SELECT * +FROM mv_laporan_penjualan +WHERE tanggal = '2025-12-01'; +``` + +Cari: + +* `Index Scan` +* `Index Only Scan` + +--- + +## πŸ”₯ Checklist Cepat (Best Practice) + +βœ… Selalu buat index +βœ… Buat **UNIQUE index** jika pakai CONCURRENTLY +βœ… Composite index > banyak index kecil +βœ… Partial index untuk data populer +βœ… ANALYZE setelah refresh +βœ… Jangan jadikan MV β€œtabel serba guna” + +--- + +Kalau mau, saya bisa: + +* βš™οΈ Buatkan **template index berdasarkan query kamu** +* πŸ“Š Analisis `EXPLAIN ANALYZE` hasilmu +* ⏱️ Rekomendasi jadwal refresh (harian/jam) + +Kirim saja **struktur MV + contoh query yang sering dipakai**.