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 cela se produit, j'aimerais mettre à jour rapidement ses données.
Voici ce à quoi je pensais jusqu'à présent:
Il y a une vue matérialisée mat_view
qui tire ses données des tables table1
et table2
en utilisant une déclaration de jointure.
Chaque fois que quelque chose dans table1
ou table2
changements, j’ai déjà un déclencheur qui met à jour une petite table de configuration config
composée de
table_name | mat_view_name | need_update
-----------+---------------+------------
table1 | mat_view | TRUE/FALSE
table2 | mat_view | TRUE/FALSE
Donc, si quelque chose dans table1
changements (il y a un déclencheur sur UPDATE et sur DELETE pour chaque instruction), le champ need_update
dans la première ligne est défini sur TRUE
. C'est la même chose pour table2
et la deuxième rangée.
Évidemment, si need_update
est VRAI, la vue matérialisée doit être actualisée.
UPDATE : Puisque les vues matérialisées ne supportent pas les règles (comme @pozs mentionné dans un commentaire ci-dessous), j'irais encore plus loin. Je créerais une vue factice v_mat_view
avec la définition "SELECT * FROM mat_view
". Lorsque l'utilisateur effectue une opération SELECT sur cette vue, je dois créer une règle ON SELECT qui effectue les opérations suivantes:
mat_view
devrait être mis à jour (SELECT 1 FROM config WHERE mat_view_name='mat_view' AND need_update=TRUE
)need_update
drapeau avec UPDATE config SET need_update=FALSE where mat_view_name='mat_view'
REFRESH MATERIALIZED VIEW mat_view
mat_view
comme cible.PDATE2: J'ai essayé de créer les étapes ci-dessus:
Créez 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 à partir 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.
# 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 dans la mat_view elle-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, en gros, ça marche, mais la performance peut être un problème.
Quelqu'un a de meilleures idées? Sinon, je devrais l'implémenter d'une manière ou d'une autre dans la logique de l'application ou pire: exécuter un simple cronjob qui s'exécute toutes les minutes environ. :-(
PostgreSQL 9.4 a ajouté REFRESH CONCURRENTLY
en vues matérialisées.
C'est peut-être ce que vous recherchez lorsque vous décrivez la tentative d'installation d'une mise à jour asynchrone de la vue matérialisée.
Les utilisateurs qui sélectionnent dans la vue matérialisée verront des données incorrectes jusqu'à la fin de l'actualisation, mais dans de nombreux scénarios utilisant une vue matérialisée, il s'agit d'un compromis acceptable.
Utilisez un déclencheur de niveau instruction qui surveille les modifications apportées dans les tables sous-jacentes, puis actualise la vue matérialisée simultanément.
Vous devez actualiser la vue dans les déclencheurs après insertion/mise à jour/suppression/tronquage pour chaque instruction 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 peut ê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 correspond parfaitement à vos besoins.
Pour réaliser actualisation différée d'une vue matérialisée, vous devez disposer de l'une des fonctionnalités suivantes:
Postgres n'en a aucune, il semble donc qu'il n'y a pas de solution claire postgres.
En prenant cela en compte, je considérerais une fonction wrapper pour selects sur mat_view, par ex.
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 cela est acceptable dans la pratique, cela dépend de détails que je ne connais pas.