PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Wie kann ich sicherstellen, dass eine materialisierte Ansicht immer aktuell ist?

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:

  1. Jede INSERT/UPDATE/DELETE-Operation muss die Abfrage ausführen (was möglicherweise langsam ist, wenn Sie MV in Betracht ziehen);
  2. Sogar mit CONCURRENTLY , ein REFRESH 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.