Rafraîchir une vue matérialisée automatiquement en utilisant une règle ou une notification
j'ai une vue matérialisée sur une base de données PostgreSQL 9.3 qui change rarement (environ deux fois par jour). Mais quand ce sera le cas, j'aimerais mettre à jour ses données rapidement.
Voici ce que je pensais jusqu'à présent:
Il y a une vue matérialisée mat_view
qui reçoit les données des tableaux table1
et table2
en utilisant une déclaration de jointure.
Chaque fois que quelque chose dans table1
ou table2
modifications, j'ai déjà un déclencheur qui met à jour une petite configuration la table config
consistant en
table_name | mat_view_name | need_update
-----------+---------------+------------
table1 | mat_view | TRUE/FALSE
table2 | mat_view | TRUE/FALSE
Donc, si quelque chose dans table1
change (il y a un déclencheur sur la mise à JOUR et SUPPRESSION pour chaque énoncé), le champ need_update
dans la première ligne est réglé sur TRUE
.
Il en va de même pour table2
et la deuxième ligne.
Évidemment, si need_update
est vrai, alors la vue matérialisée doit être rafraîchie.
UPDATE:
Depuis les vues matérialisées ne supportent pas les règles( comme @pozs mentionné dans un commentaire ci-dessous), I serait aller un peu plus loin. J'aimerais créer un mannequin vue v_mat_view
de la définition de "SELECT * FROM mat_view
". Quand l'utilisateur fait un SELECT sur cette vue, je dois créer une règle sur SELECT qui fait ce qui suit:
- vérifier si
mat_view
devrait être mis à jour (SELECT 1 FROM config WHERE mat_view_name='mat_view' AND need_update=TRUE
) - réinitialiser le
need_update
drapeau avecUPDATE config SET need_update=FALSE where mat_view_name='mat_view'
REFRESH MATERIALIZED VIEW mat_view
- et enfin faire l'original de l'instruction SELECT, mais avec
mat_view
comme la cible.
UPDATE2: J'ai essayé de créer les étapes ci-dessus:
Créer une fonction qui gère les quatre points mentionnés ci-dessus:
CREATE OR REPLACE FUNCTION mat_view_selector()
RETURNS SETOF mat_view AS $body$
BEGIN
-- here is checking whether to refresh the mat_view
-- then return the select:
RETURN QUERY SELECT * FROM mat_view;
END;
$body$ LANGUAGE plpgsql;
Créer la vue v_mat_view
qui sélectionne vraiment de la fonction mat_view_selector
:
CREATE TABLE v_mat_view AS SELECT * from mat_view LIMIT 1;
DELETE FROM v_mat_view;
CREATE RULE "_RETURN" AS
ON SELECT TO v_mat_view
DO INSTEAD
SELECT * FROM mat_view_selector();
-- this also converts the empty table 'v_mat_view' into a view.
le résultat n'est pas satisfaisant:
# explain analyze select field1 from v_mat_view where field2 = 44;
QUERY PLAN
Function Scan on mat_view_selector (cost=0.25..12.75 rows=5 width=4)
(actual time=15.457..18.048 rows=1 loops=1)
Filter: (field2 = 44)
Rows Removed by Filter: 20021
Total runtime: 31.753 ms
par rapport à la sélection à partir du mat_view lui-même:
# explain analyze select field1 from mat_view where field2 = 44;
QUERY PLAN
Index Scan using mat_view_field2 on mat_view (cost=0.29..8.30 rows=1 width=4)
(actual time=0.015..0.016 rows=1 loops=1)
Index Cond: (field2 = 44)
Total runtime: 0.036 ms
donc essentiellement cela fonctionne, mais la performance peut être un problème.
quelqu'un a de meilleures idées? Sinon, je devrais l'implémenter dans la logique de l'application ou pire: lancer un cronjob simple qui court toutes les minutes. :-(
2 réponses
PostgreSQL 9.4 ajouté REFRESH CONCURRENTLY
à des vues matérialisées.
C'est peut-être ce que vous cherchez quand vous décrivez essayer de configurer une mise à jour asynchrone de la vue matérialisée.
les utilisateurs sélectionnant à partir de la vue matérialisée verront des données incorrectes jusqu'à la fin du rafraîchissement, mais dans de nombreux scénarios qui utilisent une vue matérialisée, c'est un compromis acceptable.
Utilisez un déclencheur de niveau d'énoncé qui surveille les tables sous-jacentes pour tout changement et puis rafraîchit la vue matérialisée simultanément.
vous devriez rafraîchir la vue dans triggers après insérer/update/delete/truncate pour chaque énoncé sur table1
et table2
.
create or replace function refresh_mat_view()
returns trigger language plpgsql
as $$
begin
refresh materialized view mat_view;
return null;
end $$;
create trigger refresh_mat_view
after insert or update or delete or truncate
on table1 for each statement
execute procedure refresh_mat_view();
create trigger refresh_mat_view
after insert or update or delete or truncate
on table2 for each statement
execute procedure refresh_mat_view();
De cette façon, votre vue matérialisée est toujours à jour. Cette solution simple pourrait être difficile à accepter avec des insertions/mises à jour fréquentes et des sélections sporadiques. Dans votre cas (change rarement environ deux fois par jour) il convient idéalement à vos besoins.
Pour réaliser rafraîchissement différé d'une vue matérialisée vous avez besoin l'une des caractéristiques suivantes:
- asynchrone déclencheur
- déclencher avant de sélectionner
- règle sur les sélectionner avant
Postgres n'en a pas, il semble donc qu'il n'y en ait pas clear solution de postgres.
compte tenu de cela, je considérerais une fonction de wrapper pour les sélections sur mat_view, par exemple
CREATE OR REPLACE FUNCTION select_from_mat_view(where_clause text)
RETURNS SETOF mat_view AS $body$
BEGIN
-- here is checking whether to refresh the mat_view
-- then return the select:
RETURN QUERY EXECUTE FORMAT ('SELECT * FROM mat_view %s', where_clause);
END;
$body$ LANGUAGE plpgsql;
si elle est acceptable dans la pratique dépend de détails que je ne sais pas sur.