Je dois invoquer
REFRESH MATERIALIZED VIEW
à chaque modification des tables concernées, n'est-ce pas ?
Oui, PostgreSQL par lui-même ne l'appellera jamais automatiquement, vous devez le faire d'une manière ou d'une autre.
Comment dois-je procéder ?
De nombreuses façons d'y parvenir. Avant de donner quelques exemples, gardez à l'esprit que REFRESH MATERIALIZED VIEW
la commande bloque la vue en mode AccessExclusive, donc pendant qu'elle fonctionne, vous ne pouvez même pas faire SELECT
sur la table.
Bien que, si vous êtes dans la version 9.4 ou plus récente, vous pouvez lui donner le CONCURRENTLY
choix :
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
Cela acquerra un ExclusiveLock et ne bloquera pas SELECT
requêtes, mais peut avoir une surcharge plus importante (dépend de la quantité de données modifiées, si peu de lignes ont changé, cela peut être plus rapide). Bien que vous ne puissiez toujours pas exécuter deux REFRESH
commandes simultanément.
Actualiser manuellement
C'est une option à considérer. En particulier dans les cas de chargement de données ou de mises à jour par lots (par exemple, un système qui ne charge que des tonnes d'informations/données après de longues périodes), il est courant d'avoir des opérations à la fin pour modifier ou traiter les données, vous pouvez donc simplement inclure un REFRESH
opération à la fin de celui-ci.
Planification de l'opération REFRESH
La première option, largement utilisée, consiste à utiliser un système de planification pour invoquer l'actualisation. Par exemple, vous pouvez configurer la même chose dans une tâche cron :
*/30 * * * * psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv"
Et ensuite votre vue matérialisée sera rafraichie toutes les 30 minutes.
Considérations
Cette option est vraiment bonne, spécialement avec CONCURRENTLY
option, mais seulement si vous pouvez accepter que les données ne soient pas à jour à 100 % tout le temps. Gardez à l'esprit que même avec ou sans CONCURRENTLY
, le REFRESH
La commande doit exécuter l'intégralité de la requête, vous devez donc prendre le temps nécessaire pour exécuter la requête interne avant de considérer le temps de planifier le REFRESH
.
Rafraîchir avec un déclencheur
Une autre option consiste à appeler le REFRESH MATERIALIZED VIEW
dans une fonction déclencheur, comme ceci :
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY my_mv;
RETURN NULL;
END;
$$;
Ensuite, dans n'importe quelle table qui implique des modifications sur la vue, vous faites :
CREATE TRIGGER tg_refresh_my_mv AFTER INSERT OR UPDATE OR DELETE
ON table_name
FOR EACH STATEMENT EXECUTE PROCEDURE tg_refresh_my_mv();
Considérations
Il comporte des pièges critiques en termes de performances et de simultanéité :
- Toute opération INSERT/UPDATE/DELETE devra exécuter la requête (ce qui peut être lent si vous envisagez MV) ;
- Même avec
CONCURRENTLY
, unREFRESH
bloque toujours un autre, donc tout INSERT/UPDATE/DELETE sur les tables concernées sera sérialisé.
La seule situation dans laquelle je peux penser que c'est une bonne idée, c'est si les changements sont vraiment rares.
Actualiser en utilisant LISTEN/NOTIFY
Le problème avec l'option précédente est qu'elle est synchrone et impose un gros surcoût à chaque opération. Pour améliorer cela, vous pouvez utiliser un déclencheur comme avant, mais cela n'appelle qu'un NOTIFY
opération :
CREATE OR REPLACE FUNCTION tg_refresh_my_mv()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
NOTIFY refresh_mv, 'my_mv';
RETURN NULL;
END;
$$;
Ainsi, vous pouvez créer une application qui reste connectée et utilise LISTEN
opération pour identifier la nécessité d'appeler REFRESH
. Un beau projet que vous pouvez utiliser pour tester cela est pgsidekick, avec ce projet, vous pouvez utiliser un script shell pour faire LISTEN
, afin que vous puissiez programmer le REFRESH
comme :
pglisten --listen=refresh_mv --print0 | xargs -0 -n1 -I? psql -d your_database -c "REFRESH MATERIALIZED VIEW CONCURRENTLY ?;"
Ou utilisez pglater
(également dans pgsidekick
) pour vous assurer de ne pas appeler REFRESH
très souvent. Par exemple, vous pouvez utiliser le déclencheur suivant pour le rendre REFRESH
, mais en 1 minute (60 secondes) :
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;
$$;
Donc, il n'appellera pas REFRESH
en moins de 60 secondes d'intervalle, et aussi si vous NOTIFY
plusieurs fois en moins de 60 secondes, le REFRESH
ne sera déclenché qu'une seule fois.
Considérations
En tant qu'option cron, celle-ci n'est également bonne que si vous pouvez supporter un peu de données obsolètes, mais cela a l'avantage que le REFRESH
est appelé uniquement lorsque cela est vraiment nécessaire, vous avez donc moins de temps système et les données sont également mises à jour plus près du moment où elles sont nécessaires.
OBS :Je n'ai pas encore vraiment essayé les codes et les exemples, donc si quelqu'un trouve une erreur, une faute de frappe ou l'essaie et fonctionne (ou non), veuillez me le faire savoir.