Ich muss REFRESH MATERIALIZED VIEW
aufrufen bei jeder Änderung an den beteiligten Tabellen, richtig?
Ja, PostgreSQL selbst wird es niemals automatisch aufrufen, Sie müssen es irgendwie tun.
Wie soll ich vorgehen?
Viele Möglichkeiten, dies zu erreichen. Bevor Sie einige Beispiele geben, denken Sie daran, dass REFRESH MATERIALIZED VIEW
Der Befehl blockiert die Ansicht im AccessExclusive-Modus, sodass Sie während der Ausführung nicht einmal SELECT
ausführen können auf dem Tisch.
Wenn Sie sich jedoch in Version 9.4 oder neuer befinden, können Sie ihm den CONCURRENTLY
geben Möglichkeit:
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
Dadurch wird ein ExclusiveLock erworben und SELECT
wird nicht blockiert Abfragen, kann aber einen größeren Overhead haben (abhängig von der Menge der geänderten Daten, wenn sich nur wenige Zeilen geändert haben, kann es schneller sein). Obwohl Sie immer noch nicht zwei REFRESH
ausführen können Befehle gleichzeitig.
Manuell aktualisieren
Es ist eine zu prüfende Option. Besonders beim Laden von Daten oder Stapelaktualisierungen (z. B. ein System, das erst nach langer Zeit Tonnen von Informationen/Daten lädt) ist es üblich, am Ende Operationen zu haben, um die Daten zu ändern oder zu verarbeiten, sodass Sie einfach einen REFRESH
Operation am Ende.
Planung des REFRESH-Vorgangs
Die erste und weit verbreitete Option besteht darin, ein Planungssystem zu verwenden, um die Aktualisierung aufzurufen, zum Beispiel könnten Sie so etwas in einem Cron-Job konfigurieren:
*/30 * * * * psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv"
Und dann wird Ihre materialisierte Ansicht alle 30 Minuten aktualisiert.
Überlegungen
Diese Option ist wirklich gut, besonders mit CONCURRENTLY
Option, aber nur, wenn Sie akzeptieren können, dass die Daten nicht immer zu 100 % aktuell sind. Beachten Sie, dass auch mit oder ohne CONCURRENTLY
, das REFRESH
Befehl muss die gesamte Abfrage ausführen, daher müssen Sie sich die Zeit nehmen, die zum Ausführen der inneren Abfrage erforderlich ist, bevor Sie die Zeit zum Planen des REFRESH
in Betracht ziehen .
Aktualisierung mit einem Trigger
Eine weitere Möglichkeit ist der Aufruf der REFRESH MATERIALIZED VIEW
in einer Trigger-Funktion wie folgt:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
RETURN NULL;
END;
$$;
Dann tun Sie in jeder Tabelle, die Änderungen an der Ansicht beinhaltet:
CREATE TRIGGER tg_refresh_my_mv AFTER INSERT OR UPDATE OR DELETE
ON table_name
FOR EACH STATEMENT EXECUTE PROCEDURE tg_refresh_my_mv();
Überlegungen
Es hat einige kritische Fallstricke für Leistung und Parallelität:
- Jede INSERT/UPDATE/DELETE-Operation muss die Abfrage ausführen (was möglicherweise langsam ist, wenn Sie MV in Betracht ziehen);
- Sogar mit
CONCURRENTLY
, einREFRESH
blockiert immer noch eine andere, also werden alle INSERT/UPDATE/DELETE auf den beteiligten Tabellen serialisiert.
Die einzige Situation, die ich für eine gute Idee halten kann, ist, wenn die Änderungen wirklich selten sind.
Mit LISTEN/NOTIFY aktualisieren
Das Problem bei der vorherigen Option besteht darin, dass sie synchron ist und bei jeder Operation einen großen Overhead auferlegt. Um dies zu verbessern, können Sie wie zuvor einen Trigger verwenden, der jedoch nur ein NOTIFY
aufruft Betrieb:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NOTIFY refresh_mv, 'my_mv';
RETURN NULL;
END;
$$;
Dann können Sie eine Anwendung erstellen, die verbunden bleibt und LISTEN
verwendet Operation, um die Notwendigkeit zu identifizieren, REFRESH
aufzurufen . Ein nettes Projekt, das Sie verwenden können, um dies zu testen, ist pgsidekick, mit diesem Projekt können Sie ein Shell-Skript verwenden, um LISTEN
auszuführen , damit Sie das REFRESH
planen können als:
pglisten --listen=refresh_mv --print0 | xargs -0 -n1 -I? psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY ?;"
Oder verwenden Sie pglater
(auch in pgsidekick
), um sicherzustellen, dass Sie REFRESH
nicht aufrufen sehr oft. Sie können zum Beispiel den folgenden Trigger verwenden, um es REFRESH
zu machen , aber innerhalb von 1 Minute (60 Sekunden):
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;
$$;
Es wird also REFRESH
nicht aufgerufen in weniger als 60 Sekunden Abstand, und auch, wenn Sie NOTIFY
viele Male in weniger als 60 Sekunden das REFRESH
wird nur einmal ausgelöst.
Überlegungen
Als Cron-Option ist diese auch nur gut, wenn man mit ein wenig veralteten Daten auskommen kann, aber das hat den Vorteil, dass die REFRESH
wird nur aufgerufen, wenn es wirklich benötigt wird, sodass Sie weniger Overhead haben, und außerdem werden die Daten zeitnaher aktualisiert, wenn sie benötigt werden.
OBS:Ich habe die Codes und Beispiele noch nicht wirklich ausprobiert, also wenn jemand einen Fehler oder Tippfehler findet oder es versucht und funktioniert (oder nicht), lass es mich bitte wissen.