PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Come posso assicurarmi che una vista materializzata sia sempre aggiornata?

Dovrò invocare REFRESH MATERIALIZED VIEW su ogni modifica ai tavoli coinvolti, giusto?

Sì, PostgreSQL da solo non lo chiamerà mai automaticamente, devi farlo in qualche modo.

Come devo fare per farlo?

Molti modi per raggiungere questo obiettivo. Prima di fare alcuni esempi, tieni presente che REFRESH MATERIALIZED VIEW il comando blocca la visualizzazione in modalità AccessExclusive, quindi mentre funziona, non puoi nemmeno fare SELECT sul tavolo.

Tuttavia, se sei nella versione 9.4 o successiva, puoi assegnargli il CONCURRENTLY opzione:

REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;

Questo acquisirà un ExclusiveLock e non bloccherà SELECT query, ma potrebbe avere un sovraccarico maggiore (dipende dalla quantità di dati modificati, se sono state modificate poche righe, potrebbe essere più veloce). Anche se non puoi ancora eseguire due REFRESH comandi contemporaneamente.

Aggiorna manualmente

È un'opzione da considerare. Specialmente nei casi di caricamento dati o aggiornamenti batch (ad es. un sistema che carica solo tonnellate di informazioni/dati dopo lunghi periodi di tempo) è comune avere operazioni alla fine per modificare o elaborare i dati, quindi puoi semplicemente includere un REFRESH operazione alla fine.

Programmazione dell'operazione di AGGIORNAMENTO

La prima e ampiamente utilizzata opzione è utilizzare un sistema di pianificazione per invocare l'aggiornamento, ad esempio, puoi configurare simili in un lavoro cron:

*/30 * * * * psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv"

E poi la tua vista materializzata verrà aggiornata ogni 30 minuti.

Considerazioni

Questa opzione è davvero buona, specialmente con CONCURRENTLY opzione, ma solo se puoi accettare che i dati non siano sempre aggiornati al 100%. Tieni presente che anche con o senza CONCURRENTLY , il REFRESH il comando deve eseguire l'intera query, quindi devi prenderti il ​​tempo necessario per eseguire la query interna prima di considerare il tempo per pianificare il REFRESH .

Rinfresco con un trigger

Un'altra opzione è chiamare il REFRESH MATERIALIZED VIEW in una funzione trigger, come questa:

CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
    REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
    RETURN NULL;
END;
$$;

Quindi, in qualsiasi tabella che comporta modifiche alla vista, fai:

CREATE TRIGGER tg_refresh_my_mv AFTER INSERT OR UPDATE OR DELETE
ON table_name
FOR EACH STATEMENT EXECUTE PROCEDURE tg_refresh_my_mv();

Considerazioni

Presenta alcune insidie ​​critiche per le prestazioni e la concorrenza:

  1. Qualsiasi operazione INSERT/UPDATE/DELETE dovrà eseguire la query (che è possibile lenta se stai considerando MV);
  2. Anche con CONCURRENTLY , un REFRESH ne blocca ancora un altro, quindi qualsiasi INSERT/UPDATE/DELETE sulle tabelle coinvolte verrà serializzato.

L'unica situazione che posso pensare che sia una buona idea è se i cambiamenti sono davvero rari.

Aggiorna utilizzando LISTEN/NOTIFY

Il problema con l'opzione precedente è che è sincrona e impone un grande sovraccarico ad ogni operazione. Per migliorare ciò, puoi utilizzare un trigger come prima, ma che chiama solo un NOTIFY operazione:

CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
    NOTIFY refresh_mv, 'my_mv';
    RETURN NULL;
END;
$$;

Quindi puoi creare un'applicazione che rimanga connessa e utilizzi LISTEN operazione per identificare la necessità di chiamare REFRESH . Un bel progetto che puoi usare per testarlo è pgsidekick, con questo progetto puoi usare lo script della shell per fare LISTEN , così puoi programmare il REFRESH come:

pglisten --listen=refresh_mv --print0 | xargs -0 -n1 -I? psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY ?;"

Oppure usa pglater (anche all'interno di pgsidekick ) per assicurarti di non chiamare REFRESH molto spesso. Ad esempio, puoi utilizzare il seguente trigger per renderlo REFRESH , ma entro 1 minuto (60 secondi):

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;
$$;

Quindi non chiamerà REFRESH in meno di 60 secondi di distanza, e anche se NOTIFY molte volte in meno di 60 secondi, il REFRESH verrà attivato solo una volta.

Considerazioni

Come l'opzione cron, anche questa va bene solo se puoi mettere a nudo un po' di dati obsoleti, ma questo ha il vantaggio che REFRESH viene chiamato solo quando realmente necessario, quindi hai meno sovraccarico e inoltre i dati vengono aggiornati più vicino a quando necessario.

OBS:Non ho ancora provato i codici e gli esempi, quindi se qualcuno trova un errore, un errore di battitura o lo prova e funziona (o meno), per favore fatemelo sapere.