Aktualisieren Sie die materalisierte Ansicht in PostgreSQL schrittweise

33

Ist es möglich, eine materialisierte Ansicht in PostgreSQL schrittweise zu aktualisieren, dh nur für die Daten, die neu sind oder sich geändert haben?

Betrachten Sie diese Tabelle und materialisierte Ansicht:

CREATE TABLE graph (
   xaxis integer NOT NULL,
   value integer NOT NULL,
);

CREATE MATERIALIZED VIEW graph_avg AS 
SELECT xaxis, AVG(value)
FROM graph
GROUP BY xaxis

In regelmäßigen Abständen werden neue Werte hinzugefügt graphoder ein vorhandener Wert aktualisiert. Ich möchte die Ansicht graph_avgalle paar Stunden nur für die aktualisierten Werte aktualisieren. In PostgreSQL 9.3 wird jedoch die gesamte Tabelle aktualisiert. Das ist ziemlich zeitaufwändig. Die nächste Version 9.4 ermöglicht ein CONCURRENTUpdate, aktualisiert jedoch weiterhin die gesamte Ansicht. Bei Hunderten von Millionen Zeilen dauert dies einige Minuten.

Was ist ein guter Weg, um den Überblick über aktualisierte und neue Werte zu behalten und die Ansicht nur teilweise zu aktualisieren?

user4150760
quelle

Antworten:

22

Sie können jederzeit eine eigene Tabelle implementieren, die als "materialisierte Ansicht" dient. Das mussten Sie tun, bevor MATERIALIZED VIEWPostgres 9.3 so oder so implementiert wurde.

Beispielsweise können Sie eine Ebene erstellen VIEW:

CREATE VIEW graph_avg_view AS 
SELECT xaxis, AVG(value) AS avg_val
FROM   graph
GROUP  BY xaxis;

Und materialisieren Sie das Ergebnis als Ganzes einmal oder wann immer Sie von vorne anfangen müssen:

CREATE TABLE graph_avg AS
SELECT * FROM graph_avg_view

(Oder die Verwendung SELECTAnweisung direkt, ohne ein zu schaffen VIEW.)
Dann, abhängig von undisclosed Details Ihrer Verwendung Fall, könnten Sie DELETE/ UPDATE/ INSERTÄnderungen manuell.

Eine grundlegende DML-Anweisung mit datenmodifizierenden CTEs für Ihre Tabelle wie folgt :

Unter der Annahme , sonst niemand versucht zu schreiben , um graph_avggleichzeitig (Lesen ist kein Problem):

WITH del AS (
   DELETE FROM graph_avg t
   WHERE  NOT EXISTS (SELECT 1 FROM graph_avg_view v WHERE v.xaxis = v.xaxis);
   )
, upd AS (
   UPDATE graph_avg t
   FROM   graph_avg_view v
   WHERE  t.xaxis = v.xaxis
   AND    t.avg_val <> v.avg_val
   )
INSERT INTO graph_avg t
SELECT *
FROM   graph_avg_view v
LEFT   JOIN graph_avg t USING (xaxis)
WHERE  t.xaxis IS NULL;

Dies sollte aber höchstwahrscheinlich optimiert werden.

Grundrezept:

  • Fügen Sie Ihrer Basistabelle eine timestampStandardspalte hinzu now(). Nennen wir es ts.
    • Wenn Sie Aktualisierungen haben, fügen Sie einen Auslöser hinzu, um den aktuellen Zeitstempel für jede Aktualisierung festzulegen, die entweder xaxisoder geändert wird value.
  • Erstellen Sie eine winzige Tabelle, um sich den Zeitstempel Ihres letzten Schnappschusses zu merken. Nennen wir es mv:

    CREATE TABLE mv (
       tbl text PRIMARY KEY
     , ts timestamp NOT NULL DEFAULT '-infinity'
    ); -- possibly more details
  • Erstellen Sie diesen mehrspaltigen Teilindex:

    CREATE INDEX graph_mv_latest ON graph (xaxis, value)
    WHERE  ts >= '-infinity';
  • Verwenden Sie den Zeitstempel des letzten Snapshots als Vergleichselement in Ihren Abfragen, um den Snapshot mit perfekter Indexnutzung zu aktualisieren.

  • Löschen Sie am Ende der Transaktion den Index und erstellen Sie ihn neu, wobei der Transaktionszeitstempel den Zeitstempel im Indexprädikat (anfangs '-infinity') ersetzt, den Sie auch in Ihrer Tabelle speichern. Alles in einer Transaktion.

  • Beachten Sie, dass der Teilindex Abdeckung ist groß INSERTund UPDATEOperationen, aber nicht DELETE. Um dies abzudecken, müssen Sie die gesamte Tabelle betrachten. Alles hängt von den genauen Anforderungen ab.

Erwin Brandstetter
quelle
Vielen Dank für die Klarheit in Bezug auf materialisierte Ansichten und den Vorschlag einer alternativen Antwort.
user4150760
13

Gleichzeitiges Update (Postgres 9.4)

Postgres 9.4 ist zwar kein inkrementelles Update, wie Sie es gewünscht haben, bietet jedoch eine neue Funktion für gleichzeitige Updates .

Um das Dokument zu zitieren ...

Vor PostgreSQL 9.4 bedeutete das Aktualisieren einer materialisierten Ansicht das Sperren der gesamten Tabelle und damit das Verhindern von Abfragen. Wenn eine Aktualisierung lange Zeit in Anspruch nahm, um die exklusive Sperre zu erhalten (während sie auf Abfragen wartet, die sie zum Beenden verwenden), wurde sie der Reihe nach gesperrt hält nachfolgende Abfragen auf. Dies kann jetzt mit dem Schlüsselwort CONCURRENTLY gemildert werden:

 postgres=# REFRESH MATERIALIZED VIEW CONCURRENTLY mv_data;

Für die materialisierte Ansicht muss jedoch ein eindeutiger Index vorhanden sein. Anstatt die materialisierte Ansicht zu sperren, wird stattdessen eine temporär aktualisierte Version davon erstellt, die beiden Versionen verglichen und dann INSERTs und DELETEs mit der materialisierten Ansicht verglichen, um den Unterschied anzuwenden. Dies bedeutet, dass Abfragen die materialisierte Ansicht weiterhin verwenden können, während sie aktualisiert wird. Im Gegensatz zu seiner nicht gleichzeitigen Form werden Tupel nicht eingefroren, und es muss aufgrund der oben genannten DELETEs VAKUUMIERT werden, wodurch tote Tupel zurückbleiben.

Dieses gleichzeitige Update führt noch eine vollständige neue Abfrage durch (nicht inkrementell). CONCURRENTLY spart also nicht die gesamte Rechenzeit, sondern minimiert nur die Zeit, die Ihre materialisierte Ansicht für die Verwendung während der Aktualisierung nicht verfügbar ist.

Basil Bourque
quelle
11
Für einen Moment war ich aufgeregt, bis ich genau las. it instead creates a temporary updated version of it...compares the two versions- Dies bedeutet, dass die temporär aktualisierte Version immer noch eine vollständige Berechnung ist und dann den Unterschied auf die vorhandene Ansicht anwendet. Im Grunde genommen mache ich immer noch ALLE Berechnungen neu, aber nur in der temporären Tabelle.
user4150760
5
Allerdings CONCURRENTLYspart dies nicht die gesamte Rechenzeit, sondern minimiert nur die Zeit, die Ihre materialisierte Ansicht für die Verwendung während der Aktualisierung nicht verfügbar ist.
Basil Bourque