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