sql >> Database >  >> RDS >> PostgreSQL

Hoe zorg ik ervoor dat een gematerialiseerde view altijd up-to-date is?

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:

  1. Elke INSERT/UPDATE/DELETE-bewerking moet de query uitvoeren (wat mogelijk traag is als u MV overweegt);
  2. Zelfs met GELIJKTIJDIG , één VERFRISSEN 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.



  1. Hoe kolommen samenvoegen in een Postgres SELECT?

  2. Achterwaartse scan van SQL Server Index:inzicht en prestatieafstemming

  3. Converteer float naar varchar in SQL Server zonder wetenschappelijke notatie

  4. MariaDB komt naar een stad bij jou in de buurt!