web-dev-qa-db-fra.com

Actualiser la vue matérialisée incrémentielle dans PostgreSQL

Est-il possible d'actualiser une vue matérialisée de manière incrémentielle dans PostgreSQL, c'est-à-dire uniquement pour les données nouvelles ou modifiées?

Considérez cette table et vue matérialisée:

CREATE TABLE graph (
   xaxis integer NOT NULL,
   value integer NOT NULL,
);

CREATE MATERIALIZED VIEW graph_avg AS 
SELECT xaxis, AVG(value)
FROM graph
GROUP BY xaxis

Périodiquement, de nouvelles valeurs sont ajoutées à graph ou une valeur existante est mise à jour. Je souhaite rafraîchir la vue graph_avg toutes les deux heures uniquement pour les valeurs mises à jour. Cependant, dans PostgreSQL 9.3, la table entière est actualisée. Cela prend beaucoup de temps. La prochaine version 9.4 permet la mise à jour de CONCURRENT mais actualise toujours la vue entière. Avec des centaines de millions de lignes, cela prend quelques minutes.

Quel est un bon moyen de garder une trace des valeurs mises à jour et nouvelles et de ne rafraîchir la vue que partiellement?

35
user4150760

Vous pouvez toujours implémenter votre propre table servant de "vue matérialisée". C'est ainsi que nous le faisions avant MATERIALIZED VIEW a été implémenté dans Postgres 9.3.

Vous pouvez créer un simple VIEW :

CREATE VIEW graph_avg_view AS 
SELECT xaxis, AVG(value) AS avg_val
FROM   graph
GROUP  BY xaxis;

Et matérialisez le résultat une fois ou chaque fois que vous devez recommencer:

CREATE TABLE graph_avg AS
SELECT * FROM graph_avg_view;

(Ou utilisez directement l'instruction SELECT, sans créer de VIEW.)
Ensuite, selon les détails non divulgués de votre cas d'utilisation, vous pouvez DELETE/UPDATE/INSERT modifier manuellement.

Une instruction DML de base avec des CTE modificateurs de données pour votre table tel quel:

En supposant que personne d'autre n'essaye de - écrire à graph_avg Simultanément (la lecture n'est pas un problème):

WITH del AS (
   DELETE FROM graph_avg t
   WHERE  NOT EXISTS (SELECT FROM graph_avg_view WHERE xaxis = t.xaxis)
   )
, upd AS (
   UPDATE graph_avg t
   SET    avg_val = v.avg_val
   FROM   graph_avg_view v
   WHERE  t.xaxis = v.xaxis
   AND    t.avg_val <> v.avg_val
-- AND    t.avg_val IS DISTINCT FROM v.avg_val  -- alt if avg_val can be NULL
   )
INSERT INTO graph_avg t  -- no target list, whole row
SELECT v.*
FROM   graph_avg_view v
WHERE  NOT EXISTS (SELECT FROM graph_avg WHERE xaxis = v.xaxis);

Recette de base

  • Ajoutez une colonne timestamp avec la fonction par défaut now() à votre table de base. Appelons cela ts.
    • Si vous avez des mises à jour, ajoutez un déclencheur pour définir l'horodatage actuel à chaque mise à jour qui modifie xaxis ou value.
  • Créez une petite table pour mémoriser l'horodatage de votre dernier instantané. Appelons ça mv:

    CREATE TABLE mv (
       tbl text PRIMARY KEY
     , ts timestamp NOT NULL DEFAULT '-infinity'
    ); -- possibly more details
    
  • Créez cet index partiel à plusieurs colonnes:

    CREATE INDEX graph_mv_latest ON graph (xaxis, value)
    WHERE  ts >= '-infinity';
    
  • Utilisez l'horodatage de l'instantané dernier comme prédicat dans vos requêtes pour actualiser l'instantané avec une utilisation parfaite de l'index.

  • À la fin de la transaction, supprimez l'index et recréez-le avec l'horodatage de la transaction en remplaçant l'horodatage dans le prédicat d'index (initialement '-infinity'), Que vous enregistrez également dans votre table. Tout dans un transaction.

  • Notez que l'index partiel est idéal pour couvrir les opérations INSERT et UPDATE, mais pas DELETE . Pour couvrir cela, vous devez considérer l'ensemble du tableau. Tout dépend des exigences exactes.

23
Erwin Brandstetter

Mise à jour simultanée (Postgres 9.4)

Bien qu'il ne s'agisse pas d'une mise à jour incrémentielle comme vous l'avez demandé, Postgres 9.4 fournit une nouvelle fonctionnalité mise à jour simultanée .

Pour citer le doc…

Avant PostgreSQL 9.4, l'actualisation d'une vue matérialisée signifiait verrouiller la table entière, et donc empêcher tout interrogation, et si une actualisation prenait beaucoup de temps pour acquérir le verrou exclusif (en attendant que les requêtes l'utilisant se terminent), elle à son tour suspend les requêtes suivantes. Cela peut maintenant être atténué avec le mot clé CONCURRENTLY:

 postgres=# REFRESH MATERIALIZED VIEW CONCURRENTLY mv_data;

Un index unique devra cependant exister sur la vue matérialisée. Au lieu de verrouiller la vue matérialisée, il en crée une version mise à jour temporaire, compare les deux versions, puis applique INSERT et DELETE à la vue matérialisée pour appliquer la différence. Cela signifie que les requêtes peuvent toujours utiliser la vue matérialisée pendant sa mise à jour. Contrairement à sa forme non simultanée, les tuples ne sont pas figés, et il a besoin de VACUUMing en raison des SUPPRIMÉS susmentionnés qui laisseront les tuples morts derrière.

Cette mise à jour simultanée exécute toujours une nouvelle requête complète (non incrémentielle). CONCURRENTLY n'économise donc pas sur le temps de calcul global, il minimise simplement le temps pendant lequel votre vue matérialisée n'est pas disponible pendant sa mise à jour.

15
Basil Bourque