Aggiorna la vista materalizzata in modo incrementale in PostgreSQL


33

È possibile aggiornare una vista materializzata in modo incrementale in PostgreSQL, ovvero solo per i dati nuovi o modificati?

Considera questa tabella e vista materializzata:

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

Periodicamente, vengono aggiunti nuovi valori grapho viene aggiornato un valore esistente. Voglio aggiornare la vista graph_avgogni paio d'ore solo per i valori che sono stati aggiornati. Tuttavia in PostgreSQL 9.3, l'intera tabella viene aggiornata. Questo richiede molto tempo. La prossima versione 9.4 consente l' CONCURRENTaggiornamento ma aggiorna comunque l'intera vista. Con centinaia di milioni di file, questo richiede alcuni minuti.

Qual è un buon modo per tenere traccia dei valori aggiornati e nuovi e aggiornare la vista solo parzialmente?

Risposte:


22

Puoi sempre implementare la tua tabella che funge da "vista materializzata". Questo è quello che dovevi fare prima di MATERIALIZED VIEWessere implementato in Postgres 9.3 in entrambi i modi.

Ad esempio, puoi creare un semplice VIEW:

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

E materializza il risultato nel suo insieme una volta o ogni volta che è necessario ricominciare da capo:

CREATE TABLE graph_avg AS
SELECT * FROM graph_avg_view

(O utilizzare SELECTdirettamente la dichiarazione, senza creare un VIEW.)
Quindi, a seconda dei dettagli non divulgati del caso d'uso, è possibile DELETE/ UPDATE/ INSERTmodificare manualmente.

Un'istruzione DML di base con CTE che modificano i dati per la tabella come è :

Supponendo che nessun altro cerca di scrittura di graph_avgconcomitanza (lettura non è un problema):

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;

Ma questo dovrebbe probabilmente essere ottimizzato.

Ricetta base:

  • Aggiungi una timestampcolonna per impostazione predefinita now()alla tabella di base. Chiamiamolo ts.
    • Se si dispone di aggiornamenti, aggiungere un trigger per impostare il timestamp corrente con ogni aggiornamento che cambia xaxiso value.
  • Crea una piccola tabella per ricordare il timestamp della tua ultima istantanea. Chiamiamolo mv:

    CREATE TABLE mv (
       tbl text PRIMARY KEY
     , ts timestamp NOT NULL DEFAULT '-infinity'
    ); -- possibly more details
  • Crea questo indice parziale a più colonne:

    CREATE INDEX graph_mv_latest ON graph (xaxis, value)
    WHERE  ts >= '-infinity';
  • Utilizzare il timestamp dell'ultima istantanea come predicato nelle query per aggiornare l'istantanea con un utilizzo perfetto dell'indice.

  • Alla fine della transazione, rilasciare l'indice e ricrearlo con il timestamp della transazione sostituendo il timestamp nel predicato dell'indice (inizialmente '-infinity'), che si salva anche nella tabella. Tutto in una transazione.

  • Si noti che l'indice parziale è ottimo per la copertura INSERTe le UPDATEoperazioni, ma non DELETE. Per coprirlo, devi considerare l'intero tavolo. Tutto dipende da requisiti esatti.


Grazie per la chiarezza sulle opinioni materializzate e per aver suggerito una risposta alternativa.
user4150760,

13

Aggiornamento simultaneo (Postgres 9.4)

Sebbene non sia un aggiornamento incrementale come richiesto, Postgres 9.4 fornisce una nuova funzionalità di aggiornamento simultaneo .

Per citare il documento ...

Prima di PostgreSQL 9.4, aggiornare una vista materializzata significava bloccare l'intera tabella e quindi impedire a qualsiasi query di interrogarla, e se un aggiornamento impiegava molto tempo ad acquisire il blocco esclusivo (mentre attende che le query lo utilizzino per terminare), a sua volta sta trattenendo le domande successive. Questo ora può essere mitigato con la parola chiave CONCURRENTLY:

 postgres=# REFRESH MATERIALIZED VIEW CONCURRENTLY mv_data;

Tuttavia, nella vista materializzata dovrà esistere un indice univoco. Invece di bloccare la vista materializzata, crea invece una versione aggiornata temporanea di essa, confronta le due versioni, quindi applica INSERT e DELETE alla vista materializzata per applicare la differenza. Ciò significa che le query possono comunque utilizzare la vista materializzata mentre viene aggiornata. A differenza della sua forma non concorrente, le tuple non sono congelate e ha bisogno di VUOTO a causa delle suddette DELETTE che lasceranno indietro le tuple morte.

Questo aggiornamento simultaneo sta ancora eseguendo una nuova query completa (non incrementale). Quindi CONCURRENTLY non risparmia sul tempo di calcolo complessivo, riduce al minimo il tempo in cui la vista materializzata non è disponibile per l'uso durante il suo aggiornamento.


11
Per un momento sono stato eccitato fino a quando non ho letto attentamente. it instead creates a temporary updated version of it...compares the two versions- Ciò significa che la versione aggiornata temporanea è ancora un calcolo completo, quindi applica la differenza alla vista esistente. Quindi, essenzialmente, sto ancora rifacendo TUTTI i calcoli, ma solo nella tabella temporanea.
user4150760

5
Ah, vero, CONCURRENTLYnon risparmia sul tempo di calcolo complessivo, riduce al minimo il tempo in cui la vista materializzata non è disponibile per l'uso durante il suo aggiornamento.
Basil Bourque,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.