Budu muset vyvolat
REFRESH MATERIALIZED VIEW
při každé změně příslušných tabulek, že?
Ano, PostgreSQL to sám o sobě nikdy nezavolá automaticky, musíte to nějakým způsobem udělat.
Jak to mám udělat?
Mnoho způsobů, jak toho dosáhnout. Než uvedete nějaké příklady, mějte na paměti, že REFRESH MATERIALIZED VIEW
příkaz blokuje zobrazení v režimu AccessExclusive, takže když funguje, nemůžete ani provést SELECT
na stole.
Pokud jste ve verzi 9.4 nebo novější, můžete mu dát CONCURRENTLY
možnost:
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
Tím získáte ExclusiveLock a neblokujete SELECT
dotazy, ale může mít větší režii (závisí na množství změněných dat, pokud se změnilo několik řádků, může to být rychlejší). I když stále nemůžete spustit dva REFRESH
příkazy současně.
Obnovit ručně
Je to možnost ke zvážení. Speciálně v případech načítání dat nebo dávkových aktualizací (např. systém, který načítá jen tuny informací/dat po dlouhé době) je běžné mít na konci operace pro úpravu nebo zpracování dat, takže můžete jednoduše zahrnout REFRESH
operace na konci.
Naplánování operace REFRESH
První a široce používanou možností je použít nějaký plánovací systém k vyvolání obnovy, například byste mohli nakonfigurovat v úloze cron:
*/30 * * * * psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv"
A pak se váš zhmotněný pohled obnoví každých 30 minut.
Úvahy
Tato možnost je opravdu dobrá, zvláště s CONCURRENTLY
možnost, ale pouze v případě, že můžete akceptovat, že data nejsou neustále 100% aktuální. Mějte na paměti, že i s nebo bez CONCURRENTLY
, REFRESH
příkaz potřebuje spustit celý dotaz, takže musíte věnovat čas potřebný ke spuštění vnitřního dotazu, než zvážíte čas na naplánování REFRESH
.
Obnovení pomocí spouštěče
Další možností je zavolat REFRESH MATERIALIZED VIEW
ve spouštěcí funkci, jako je tato:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
RETURN NULL;
END;
$$;
Poté v jakékoli tabulce, která zahrnuje změny v pohledu, provedete:
CREATE TRIGGER tg_refresh_my_mv AFTER INSERT OR UPDATE OR DELETE
ON table_name
FOR EACH STATEMENT EXECUTE PROCEDURE tg_refresh_my_mv();
Úvahy
Má některá kritická úskalí pro výkon a souběžnost:
- Jakákoli operace INSERT/UPDATE/DELETE bude muset provést dotaz (což může být pomalé, pokud uvažujete o MV);
- Dokonce i s
CONCURRENTLY
, jedenREFRESH
stále blokuje další, takže jakékoli INSERT/UPDATE/DELETE v příslušných tabulkách budou serializovány.
Jediná situace, kterou považuji za dobrý nápad, je, pokud jsou změny opravdu vzácné.
Obnovte pomocí LISTEN/NOTIFY
Problém s předchozí možností je, že je synchronní a vyžaduje velkou režii na každou operaci. Chcete-li to zlepšit, můžete použít spouštěč jako dříve, ale volá pouze NOTIFY
operace:
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NOTIFY refresh_mv, 'my_mv';
RETURN NULL;
END;
$$;
Pak můžete vytvořit aplikaci, která zůstane připojená a bude používat LISTEN
operaci k identifikaci potřeby zavolat REFRESH
. Jeden pěkný projekt, který můžete použít k otestování, je pgsidekick, s tímto projektem můžete použít skript shellu k provedení LISTEN
, takže můžete naplánovat REFRESH
jako:
pglisten --listen=refresh_mv --print0 | xargs -0 -n1 -I? psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY ?;"
Nebo použijte pglater
(také uvnitř pgsidekick
), abyste se ujistili, že nevoláte REFRESH
velmi často. Můžete například použít následující spouštěč k tomu, aby byl REFRESH
, ale do 1 minuty (60 sekund):
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;
$$;
Nebude tedy volat REFRESH
za méně než 60 sekund od sebe a také pokud NOTIFY
mnohokrát za méně než 60 sekund, REFRESH
se spustí pouze jednou.
Úvahy
Jako možnost cronu je tato také dobrá pouze v případě, že si vystačíte s málo zastaralými daty, ale má tu výhodu, že REFRESH
je volána pouze tehdy, když je to skutečně potřeba, takže máte méně režie a také se data aktualizují blíže tomu, kdy je potřeba.
OBS:Kódy a příklady jsem ještě pořádně nezkoušel, takže pokud někdo najde chybu, překlep nebo to vyzkouší a funguje (nebo ne), dejte mi prosím vědět.