Jag måste anropa
REFRESH MATERIALIZED VIEW
på varje ändring av de berörda tabellerna, eller hur?
Ja, PostgreSQL i sig kommer aldrig att anropa det automatiskt, du måste göra det på något sätt.
Hur ska jag gå till väga?
Många sätt att uppnå detta. Innan du ger några exempel, kom ihåg att REFRESH MATERIALIZED VIEW
kommandot blockerar vyn i AccessExclusive-läge, så medan det fungerar kan du inte ens göra SELECT
på bordet.
Även om du är i version 9.4 eller senare kan du ge den CONCURRENTLY
alternativ:
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
Detta kommer att få ett ExclusiveLock och blockerar inte SELECT
frågor, men kan ha en större omkostnad (beror på mängden data som ändras, om få rader har ändrats kan det gå snabbare). Även om du fortfarande inte kan köra två REFRESH
kommandon samtidigt.
Uppdatera manuellt
Det är ett alternativ att överväga. Speciellt i fall av dataladdning eller batchuppdateringar (t.ex. ett system som bara laddar massor av information/data efter långa tidsperioder) är det vanligt att ha operationer i slutet för att modifiera eller bearbeta data, så du kan enkelt inkludera en REFRESH
operation i slutet av den.
Schemalägga REFRESH-åtgärden
Det första och ofta använda alternativet är att använda något schemaläggningssystem för att anropa uppdateringen, till exempel kan du konfigurera liknande i ett cron-jobb:
*/30 * * * * psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv"
Och sedan kommer din materialiserade vy att uppdateras var 30:e minut.
Överväganden
Det här alternativet är riktigt bra, speciellt med CONCURRENTLY
alternativet, men bara om du kan acceptera att data inte är 100 % uppdaterade hela tiden. Tänk på att även med eller utan CONCURRENTLY
, REFRESH
kommandot behöver köra hela frågan, så du måste ta den tid som behövs för att köra den inre frågan innan du överväger tiden för att schemalägga REFRESH
.
Uppdaterar med en utlösare
Ett annat alternativ är att anropa REFRESH MATERIALIZED VIEW
i en triggerfunktion, så här:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
RETURN NULL;
END;
$$;
Sedan, i alla tabeller som involverar ändringar av vyn, gör du:
CREATE TRIGGER tg_refresh_my_mv AFTER INSERT OR UPDATE OR DELETE
ON table_name
FOR EACH STATEMENT EXECUTE PROCEDURE tg_refresh_my_mv();
Överväganden
Den har några kritiska fallgropar för prestanda och samtidighet:
- Alla INSERT/UPDATE/DELETE-operationer måste köra frågan (vilket är möjligt långsamt om du överväger MV);
- Även med
CONCURRENTLY
, enREFRESH
blockerar fortfarande en annan, så alla INSERT/UPDATE/DELETE på de inblandade tabellerna kommer att serialiseras.
Den enda situation som jag kan tycka som en bra idé är om förändringarna verkligen är sällsynta.
Uppdatera med LISTEN/NOTIFY
Problemet med det tidigare alternativet är att det är synkront och medför stora omkostnader vid varje operation. För att förbättra det kan du använda en trigger som tidigare, men som bara anropar en NOTIFY
operation:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NOTIFY refresh_mv, 'my_mv';
RETURN NULL;
END;
$$;
Så då kan du bygga en applikation som håller kontakten och använder LISTEN
operation för att identifiera behovet av att anropa REFRESH
. Ett trevligt projekt som du kan använda för att testa detta är pgsidekick, med detta projekt kan du använda skalskript för att göra LISTEN
, så att du kan schemalägga REFRESH
som:
pglisten --listen=refresh_mv --print0 | xargs -0 -n1 -I? psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY ?;"
Eller använd pglater
(även inuti pgsidekick
) för att se till att du inte ringer REFRESH
väldigt ofta. Du kan till exempel använda följande trigger för att göra den REFRESH
, men inom 1 minut (60 sekunder):
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;
$$;
Så det kommer inte att anropa REFRESH
på mindre än 60 sekunders mellanrum, och även om du NOTIFY
många gånger på mindre än 60 sekunder visas REFRESH
utlöses endast en gång.
Överväganden
Som cron-alternativ är den här också bra bara om du kan blotta med lite inaktuell data, men detta har fördelen att REFRESH
anropas endast när det verkligen behövs, så du har mindre omkostnader, och även data uppdateras närmare när det behövs.
OBS:Jag har inte riktigt provat koderna och exemplen än, så om någon hittar ett fel, stavfel eller försöker och fungerar (eller inte), vänligen meddela mig.