Budu muset vyvolat
REFRESH MATERIALIZED VIEWpř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, jedenREFRESHstá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.