Add materialized_view_postgresql.MD
This commit is contained in:
commit
007407631f
|
|
@ -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**.
|
||||
Loading…
Reference in New Issue