Saya perlu memanggil
REFRESH MATERIALIZED VIEW
pada setiap perubahan pada tabel yang terlibat, bukan?
Ya, PostgreSQL dengan sendirinya tidak akan pernah memanggilnya secara otomatis, Anda perlu melakukannya dengan cara tertentu.
Bagaimana saya harus melakukan ini?
Banyak cara untuk mencapai ini. Sebelum memberikan beberapa contoh, perlu diingat bahwa REFRESH MATERIALIZED VIEW
perintah tidak memblokir tampilan dalam mode AccessExclusive, jadi saat sedang bekerja, Anda bahkan tidak dapat melakukan SELECT
di atas meja.
Meskipun, jika Anda berada di versi 9.4 atau yang lebih baru, Anda dapat memberikannya CONCURRENTLY
pilihan:
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
Ini akan memperoleh ExclusiveLock, dan tidak akan memblokir SELECT
kueri, tetapi mungkin memiliki overhead yang lebih besar (tergantung pada jumlah data yang diubah, jika beberapa baris telah berubah, maka itu mungkin lebih cepat). Meskipun Anda masih tidak dapat menjalankan dua REFRESH
perintah secara bersamaan.
Segarkan secara manual
Ini adalah pilihan untuk dipertimbangkan. Khususnya dalam kasus pemuatan data atau pembaruan batch (misalnya sistem yang hanya memuat banyak informasi/data setelah jangka waktu yang lama) biasanya memiliki operasi di akhir untuk memodifikasi atau memproses data, sehingga Anda dapat dengan mudah menyertakan REFRESH
operasi di akhir.
Menjadwalkan operasi REFRESH
Opsi pertama dan banyak digunakan adalah menggunakan beberapa sistem penjadwalan untuk menjalankan penyegaran, misalnya, Anda dapat mengonfigurasi yang serupa dalam tugas cron:
*/30 * * * * psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv"
Dan kemudian tampilan terwujud Anda akan disegarkan setiap 30 menit.
Pertimbangan
Opsi ini sangat bagus, khususnya dengan CONCURRENTLY
pilihan, tetapi hanya jika Anda dapat menerima data yang tidak 100% up to date sepanjang waktu. Perlu diingat, bahwa bahkan dengan atau tanpa CONCURRENTLY
, REFRESH
perintah memang perlu menjalankan seluruh kueri, jadi Anda harus meluangkan waktu yang diperlukan untuk menjalankan kueri dalam sebelum mempertimbangkan waktu untuk menjadwalkan REFRESH
.
Menyegarkan dengan pemicu
Pilihan lainnya adalah memanggil REFRESH MATERIALIZED VIEW
dalam fungsi pemicu, seperti ini:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
RETURN NULL;
END;
$$;
Kemudian, di tabel mana pun yang melibatkan perubahan tampilan, Anda melakukan:
CREATE TRIGGER tg_refresh_my_mv AFTER INSERT OR UPDATE OR DELETE
ON table_name
FOR EACH STATEMENT EXECUTE PROCEDURE tg_refresh_my_mv();
Pertimbangan
Ini memiliki beberapa jebakan penting untuk kinerja dan konkurensi:
- Setiap operasi INSERT/UPDATE/DELETE harus menjalankan kueri (yang mungkin lambat jika Anda mempertimbangkan MV);
- Bahkan dengan
CONCURRENTLY
, satuREFRESH
masih memblokir yang lain, jadi setiap INSERT/UPDATE/DELETE pada tabel yang terlibat akan diserialisasi.
Satu-satunya situasi yang saya pikir sebagai ide bagus adalah jika perubahannya sangat jarang terjadi.
Segarkan menggunakan LISTEN/NOTIFY
Masalah dengan opsi sebelumnya adalah bahwa opsi tersebut sinkron dan membebankan overhead yang besar pada setiap operasi. Untuk memperbaikinya, Anda dapat menggunakan pemicu seperti sebelumnya, tetapi itu hanya memanggil NOTIFY
operasi:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NOTIFY refresh_mv, 'my_mv';
RETURN NULL;
END;
$$;
Jadi Anda dapat membangun aplikasi yang tetap terhubung dan menggunakan LISTEN
operasi untuk mengidentifikasi kebutuhan untuk memanggil REFRESH
. Salah satu proyek bagus yang dapat Anda gunakan untuk menguji ini adalah pgsidekick, dengan proyek ini Anda dapat menggunakan skrip shell untuk melakukan LISTEN
, sehingga Anda dapat menjadwalkan REFRESH
sebagai:
pglisten --listen=refresh_mv --print0 | xargs -0 -n1 -I? psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY ?;"
Atau gunakan pglater
(juga di dalam pgsidekick
) untuk memastikan Anda tidak memanggil REFRESH
sangat sering. Misalnya, Anda dapat menggunakan pemicu berikut untuk membuatnya REFRESH
, tetapi dalam 1 menit (60 detik):
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NOTIFY refresh_mv, '60 REFRESH MATERIALIZED VIEW CONCURRENLTY my_mv';
RETURN NULL;
END;
$$;
Jadi itu tidak akan memanggil REFRESH
dalam waktu kurang dari 60 detik, dan juga jika Anda NOTIFY
berkali-kali dalam waktu kurang dari 60 detik, REFRESH
akan dipicu hanya sekali.
Pertimbangan
Sebagai opsi cron, opsi ini juga bagus hanya jika Anda dapat membuka dengan sedikit data basi, tetapi ini memiliki keuntungan bahwa REFRESH
dipanggil hanya saat benar-benar dibutuhkan, sehingga Anda memiliki lebih sedikit overhead, dan juga data diperbarui lebih dekat saat dibutuhkan.
OBS:Saya belum benar-benar mencoba kode dan contoh, jadi jika seseorang menemukan kesalahan, salah ketik atau mencobanya dan berhasil (atau tidak), beri tahu saya.