Ik moet
REFRESH MATERIALIZED VIEW
. aanroepen bij elke wijziging van de betrokken tabellen, toch?
Ja, PostgreSQL zelf zal het nooit automatisch aanroepen, je moet het op de een of andere manier doen.
Hoe moet ik dit doen?
Vele manieren om dit te bereiken. Voordat u enkele voorbeelden geeft, moet u er rekening mee houden dat VERFRISSE MATERIALISEERDE WEERGAVE
commando blokkeert de weergave in AccessExclusive-modus, dus terwijl het werkt, kunt u niet eens SELECT
doen op tafel.
Hoewel, als u in versie 9.4 of nieuwer bent, u deze de GEGEVEND
kunt geven optie:
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
Dit zal een ExclusiveLock verkrijgen en zal SELECT
niet blokkeren query's, maar kan een grotere overhead hebben (afhankelijk van de hoeveelheid gewijzigde gegevens, als er weinig rijen zijn gewijzigd, kan het sneller zijn). Hoewel je nog steeds geen twee REFRESH
. kunt uitvoeren commando's tegelijk.
Handmatig vernieuwen
Het is een optie om te overwegen. Vooral in het geval van het laden van gegevens of batch-updates (bijvoorbeeld een systeem dat pas na lange tijd tonnen informatie/gegevens laadt) is het gebruikelijk om bewerkingen aan het einde te hebben om de gegevens te wijzigen of te verwerken, zodat u eenvoudig een VERFRISSEN
operatie aan het einde ervan.
De REFRESH-bewerking plannen
De eerste en veelgebruikte optie is om een planningssysteem te gebruiken om de verversing aan te roepen, je zou bijvoorbeeld zoiets kunnen configureren in een cron-taak:
*/30 * * * * psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv"
En dan wordt je gematerialiseerde weergave elke 30 minuten vernieuwd.
Overwegingen
Deze optie is echt goed, vooral met GEGEVEND
optie, maar alleen als u kunt accepteren dat de gegevens niet altijd 100% up-to-date zijn. Houd er rekening mee dat zelfs met of zonder GEGEVEND
, de VERFRISSEN
commando moet de hele query uitvoeren, dus u moet de tijd nemen die nodig is om de innerlijke query uit te voeren voordat u de tijd overweegt om de REFRESH
te plannen .
Verversen met een trigger
Een andere optie is om de REFRESH MATERIALIZED VIEW
. aan te roepen in een triggerfunctie, zoals deze:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
RETURN NULL;
END;
$$;
Vervolgens doet u in elke tabel die wijzigingen in de weergave met zich meebrengt:
CREATE TRIGGER tg_refresh_my_mv AFTER INSERT OR UPDATE OR DELETE
ON table_name
FOR EACH STATEMENT EXECUTE PROCEDURE tg_refresh_my_mv();
Overwegingen
Het heeft een aantal kritische valkuilen voor prestaties en gelijktijdigheid:
- Elke INSERT/UPDATE/DELETE-bewerking moet de query uitvoeren (wat mogelijk traag is als u MV overweegt);
- Zelfs met
GELIJKTIJDIG
, éénVERFRISSEN
blokkeert nog steeds een andere, dus elke INSERT/UPDATE/DELETE op de betrokken tafels wordt geserialiseerd.
De enige situatie die ik als een goed idee kan bedenken, is als de veranderingen echt zeldzaam zijn.
Ververs met LISTEN/NOTIFY
Het probleem met de vorige optie is dat deze synchroon is en bij elke bewerking een grote overhead met zich meebrengt. Om dat te verbeteren, kun je een trigger gebruiken zoals voorheen, maar die roept alleen een NOTIFY
op bediening:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NOTIFY refresh_mv, 'my_mv';
RETURN NULL;
END;
$$;
Dus dan kun je een applicatie bouwen die verbonden blijft en gebruik maakt van LISTEN
operatie om de noodzaak te identificeren om REFRESH
te bellen . Een leuk project dat je kunt gebruiken om dit te testen is pgsidekick, met dit project kun je shellscript gebruiken om LISTEN
te doen , zodat u de REFRESH
. kunt plannen als:
pglisten --listen=refresh_mv --print0 | xargs -0 -n1 -I? psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY ?;"
Of gebruik pglater
(ook binnen pgsidekick
) om ervoor te zorgen dat u niet REFRESH
. belt heel vaak. U kunt bijvoorbeeld de volgende trigger gebruiken om het REFRESH
. te maken , maar binnen 1 minuut (60 seconden):
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;
$$;
Het zal dus niet REFRESH
. aanroepen in minder dan 60 seconden uit elkaar, en ook als u MELDIG
vele malen in minder dan 60 seconden, de REFRESH
wordt slechts één keer geactiveerd.
Overwegingen
Als cron-optie is deze ook alleen goed als je een beetje verouderde gegevens kunt dragen, maar dit heeft het voordeel dat de REFRESH
wordt alleen aangeroepen wanneer het echt nodig is, zodat u minder overhead heeft, en de gegevens worden ook dichter bij wanneer nodig bijgewerkt.
OBS:Ik heb de codes en voorbeelden nog niet echt geprobeerd, dus als iemand een fout of typefout vindt of het probeert en het werkt (of niet), laat het me dan weten.