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

Hvordan kan jeg sikre, at en materialiseret visning altid er opdateret?

Jeg bliver nødt til at kalde REFRESH MATERIALIZED VIEW på hver ændring af de involverede tabeller, ikke?

Ja, PostgreSQL i sig selv vil aldrig kalde det automatisk, du skal gøre det på en eller anden måde.

Hvordan skal jeg gøre dette?

Mange måder at opnå dette på. Inden du giver nogle eksempler, skal du huske at REFRESH MATERIALIZED VIEW kommando blokerer visningen i AccessExclusive-tilstand, så mens den virker, kan du ikke engang gøre SELECT på bordet.

Selvom du er i version 9.4 eller nyere, kan du give den CONCURRENTLY mulighed:

REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;

Dette vil erhverve en ExclusiveLock og vil ikke blokere SELECT forespørgsler, men kan have større overhead (afhænger af mængden af ​​ændrede data, hvis få rækker er ændret, så kan det være hurtigere). Selvom du stadig ikke kan køre to REFRESH kommandoer samtidigt.

Opdater manuelt

Det er en mulighed at overveje. Specielt i tilfælde af dataindlæsning eller batchopdateringer (f.eks. et system, der kun indlæser tonsvis af information/data efter lange perioder) er det almindeligt at have operationer til sidst for at ændre eller behandle dataene, så du kan nemt inkludere en REFRESH operation i slutningen af ​​det.

Planlægning af REFRESH-handlingen

Den første og udbredte mulighed er at bruge et eller andet planlægningssystem til at kalde opdateringen, for eksempel kan du konfigurere lignende i et cron-job:

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

Og så vil din materialiserede visning blive opdateret hvert 30. minut.

Overvejelser

Denne mulighed er rigtig god, især med CONCURRENTLY mulighed, men kun hvis du kan acceptere, at data ikke er 100 % opdateret hele tiden. Husk, at selv med eller uden CONCURRENTLY , REFRESH kommandoen skal køre hele forespørgslen, så du skal tage den nødvendige tid til at køre den indre forespørgsel, før du overvejer tiden til at planlægge REFRESH .

Opdater med en trigger

En anden mulighed er at kalde REFRESH MATERIALIZED VIEW i en triggerfunktion, som denne:

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

Så gør du i enhver tabel, der involverer ændringer i visningen:

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

Overvejelser

Det har nogle kritiske faldgruber for ydeevne og samtidighed:

  1. Enhver INSERT/UPDATE/DELETE operation skal udføre forespørgslen (hvilket er muligt langsomt, hvis du overvejer MV);
  2. Selv med CONCURRENTLY , én REFRESH blokerer stadig en anden, så enhver INSERT/UPDATE/DELETE på de involverede tabeller vil blive serialiseret.

Den eneste situation, jeg kan mene, er en god idé, hvis ændringerne virkelig er sjældne.

Opdater ved hjælp af LISTEN/NOTIFY

Problemet med den tidligere mulighed er, at den er synkron og pålægger en stor overhead ved hver operation. For at forbedre det kan du bruge en trigger som før, men som kun kalder 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å kan du bygge en applikation, der holder forbindelsen og bruger LISTEN operation for at identificere behovet for at kalde REFRESH . Et godt projekt, som du kan bruge til at teste dette, er pgsidekick, med dette projekt kan du bruge shell-script til at gøre LISTEN , så du kan planlægge REFRESH som:

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

Eller brug pglater (også inde i pgsidekick ) for at sikre, at du ikke ringer til REFRESH meget ofte. For eksempel kan du bruge følgende trigger til at gøre den til REFRESH , men inden for 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å den kalder ikke REFRESH på mindre end 60 sekunders mellemrum, og også hvis du NOTIFY mange gange på mindre end 60 sekunder vises REFRESH vil kun blive udløst én gang.

Overvejelser

Som cron-mulighed er denne også kun god, hvis du kan blotte med lidt forældede data, men dette har den fordel, at REFRESH kaldes kun, når det virkelig er nødvendigt, så du har mindre overhead, og også dataene opdateres tættere på, når det er nødvendigt.

OBS:Jeg har ikke rigtig prøvet koderne og eksemplerne endnu, så hvis nogen finder en fejl, tastefejl eller prøver det og virker (eller ej), så lad mig det vide.



  1. "Falal intern forbindelsesfejl" ved udførelse af en indbygget kompileret lagret procedure i SQL Server 2019 (kendt fejl)

  2. SQL IN-operatør for begyndere

  3. Sådan fungerer SQLite Quote()

  4. Problem med indsættelse ved hjælp af psycopg