de-postgres-note/materialized_view_postgresq...

3.5 KiB
Raw Blame History

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

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

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.

CREATE UNIQUE INDEX ux_mv_penjualan
ON mv_laporan_penjualan (tanggal, cabang_id);

Lalu refresh:

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:

(tanggal)
(cabang_id)

Lebih optimal:

CREATE INDEX idx_mv_penjualan_tanggal_cabang
ON mv_laporan_penjualan (tanggal, cabang_id);

Urutan kolom HARUS mengikuti pola query:

WHERE tanggal = ?
AND cabang_id = ?

4 Partial Index untuk Data Populer

Kalau query sering ambil data terbaru:

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:

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.

ANALYZE mv_laporan_penjualan;

Untuk rutin:

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:

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
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.

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.