Muszę wywołać REFRESH MATERIALIZED VIEW
po każdej zmianie w odpowiednich tabelach, prawda?
Tak, sam PostgreSQL nigdy nie wywoła go automatycznie, musisz to zrobić w jakiś sposób.
Jak mam to zrobić?
Wiele sposobów na osiągnięcie tego. Zanim podasz kilka przykładów, pamiętaj, że REFRESH MATERIALIZED VIEW
polecenie blokuje widok w trybie AccessExclusive, więc gdy działa, nie możesz nawet wykonać SELECT
na stole.
Chociaż, jeśli jesteś w wersji 9.4 lub nowszej, możesz nadać jej CONCURRENTLY
opcja:
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
Spowoduje to uzyskanie ExclusiveLock i nie zablokuje SELECT
zapytań, ale może mieć większy narzut (w zależności od ilości zmienionych danych, jeśli zmieniło się kilka wierszy, może być szybciej). Chociaż nadal nie możesz uruchomić dwóch REFRESH
komendy jednocześnie.
Odśwież ręcznie
Jest to opcja do rozważenia. Szczególnie w przypadku ładowania danych lub aktualizacji wsadowych (np. system, który ładuje tylko tony informacji/danych po długim okresie czasu) często wykonuje się operacje na końcu modyfikacji lub przetwarzania danych, więc można po prostu dołączyć REFRESH
operacja na końcu.
Planowanie operacji ODŚWIEŻ
Pierwszą i powszechnie stosowaną opcją jest użycie jakiegoś systemu planowania do wywołania odświeżania, na przykład możesz skonfigurować to w zadaniu cron:
*/30 * * * * psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv"
A następnie Twój zmaterializowany widok będzie odświeżany co 30 minut.
Rozważania
Ta opcja jest naprawdę dobra, szczególnie w przypadku CONCURRENTLY
opcja, ale tylko wtedy, gdy możesz zaakceptować, że dane nie są przez cały czas aktualne w 100%. Pamiętaj, że nawet z lub bez CONCURRENTLY
, REFRESH
polecenie musi uruchomić całe zapytanie, więc musisz poświęcić czas potrzebny na uruchomienie wewnętrznego zapytania przed rozważeniem czasu na zaplanowanie REFRESH
.
Odświeżanie za pomocą wyzwalacza
Inną opcją jest wywołanie REFRESH MATERIALIZED VIEW
w funkcji wyzwalacza, na przykład:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
RETURN NULL;
END;
$$;
Następnie w dowolnej tabeli, która obejmuje zmiany w widoku, wykonaj:
CREATE TRIGGER tg_refresh_my_mv AFTER INSERT OR UPDATE OR DELETE
ON table_name
FOR EACH STATEMENT EXECUTE PROCEDURE tg_refresh_my_mv();
Rozważania
Ma kilka krytycznych pułapek dla wydajności i współbieżności:
- Każda operacja INSERT/UPDATE/DELETE będzie musiała wykonać zapytanie (co może być powolne, jeśli rozważasz MV);
- Nawet z
CONCURRENTLY
, jednoREFRESH
nadal blokuje inny, więc wszelkie INSERT/UPDATE/DELETE w zaangażowanych tabelach zostaną zserializowane.
Jedyną sytuacją, którą uważam za dobry pomysł, jest to, że zmiany są naprawdę rzadkie.
Odśwież przy użyciu opcji LISTEN/NOTIFY
Problem z poprzednią opcją polega na tym, że jest ona synchroniczna i nakłada duże obciążenie na każdą operację. Aby to poprawić, możesz użyć wyzwalacza jak poprzednio, ale wywołuje on tylko NOTIFY
operacja:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NOTIFY refresh_mv, 'my_mv';
RETURN NULL;
END;
$$;
Możesz więc zbudować aplikację, która pozostanie połączona i używa LISTEN
operacja identyfikująca potrzebę wywołania REFRESH
. Jednym fajnym projektem, którego możesz użyć do przetestowania tego, jest pgsidekick, z tym projektem możesz użyć skryptu powłoki do wykonania LISTEN
, dzięki czemu możesz zaplanować REFRESH
jako:
pglisten --listen=refresh_mv --print0 | xargs -0 -n1 -I? psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY ?;"
Lub użyj pglater
(również wewnątrz pgsidekick
), aby upewnić się, że nie wywołasz REFRESH
bardzo często. Na przykład możesz użyć następującego wyzwalacza, aby go REFRESH
, ale w ciągu 1 minuty (60 sekund):
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;
$$;
Więc nie wywoła funkcji REFRESH
w mniej niż 60 sekund, a także jeśli NOTIFY
wielokrotnie w czasie krótszym niż 60 sekund, REFRESH
zostanie wyzwolony tylko raz.
Rozważania
Jako opcja crona, ta również jest dobra tylko wtedy, gdy możesz znieść trochę przestarzałych danych, ale ma to tę zaletę, że REFRESH
jest wywoływana tylko wtedy, gdy jest to naprawdę potrzebne, więc masz mniej narzutów, a także dane są aktualizowane bliżej, gdy jest to potrzebne.
OBS:Tak naprawdę nie wypróbowałem jeszcze kodów i przykładów, więc jeśli ktoś znajdzie błąd, literówkę lub spróbuje i działa (lub nie), daj mi znać.