PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Comment puis-je m'assurer qu'une vue matérialisée est toujours à jour ?

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é :

  1. Toute opération INSERT/UPDATE/DELETE devra exécuter la requête (ce qui peut être lent si vous envisagez MV) ;
  2. Même avec CONCURRENTLY , un REFRESH 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.