de-postgres-note/materialized_view_postgresq...

280 lines
5.2 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Ya, **PostgreSQL memiliki fitur *Materialized View***.
### Apa itu Materialized View di PostgreSQL?
**Materialized View** adalah objek database yang **menyimpan hasil query secara fisik** (seperti tabel), bukan menjalankan ulang query setiap kali dipanggil seperti *VIEW* biasa.
### Perbedaan VIEW vs MATERIALIZED VIEW
| Aspek | VIEW | MATERIALIZED VIEW |
| ---------------- | -------------------------- | ----------------- |
| Penyimpanan data | Tidak disimpan | Disimpan fisik |
| Kecepatan query | Lebih lambat (query ulang) | Lebih cepat |
| Update otomatis | Selalu real-time | ❌ Tidak otomatis |
| Perlu refresh | ❌ | ✅ Ya |
---
### Contoh penggunaan
```sql
CREATE MATERIALIZED VIEW laporan_penjualan AS
SELECT
tanggal,
SUM(total) AS total_penjualan
FROM penjualan
GROUP BY tanggal;
```
Mengambil data:
```sql
SELECT * FROM laporan_penjualan;
```
---
### Refresh data (WAJIB agar update)
```sql
REFRESH MATERIALIZED VIEW laporan_penjualan;
```
Tanpa mengunci (PostgreSQL ≥ 9.4, wajib ada index UNIQUE):
```sql
REFRESH MATERIALIZED VIEW CONCURRENTLY laporan_penjualan;
```
---
### Kapan sebaiknya pakai Materialized View?
✅ Query berat (JOIN besar, agregasi)
✅ Data laporan / dashboard
✅ Data tidak harus real-time
❌ Data yang sering berubah dan harus langsung update
---
### Kekurangan
* Data bisa **tidak up-to-date**
* Perlu mekanisme **refresh manual / cron**
* Memakai **storage tambahan**
---
Kalau mau, saya bisa jelaskan:
* 🔁 cara auto-refresh pakai **cron / pg_cron**
* ⚡ strategi performa (index materialized view)
* 🆚 kapan pilih materialized view vs table biasa
Tinggal bilang mau fokus ke yang mana.
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**.