web-dev-qa-db-fra.com

Plusieurs "Refresh Materialized View": Comment gérer?

J'ai une grande base de données Postgres avec de nombreuses tables, certaines avec des dizaines de millions de rangées. Plusieurs processus de travail mettent à jour la base de données simultanément. Pour des recherches plus rapides, les données pertinentes sont compilées dans une vue matérialisée.

Il existe peut-être plusieurs processus parallèles écrivant dans la base de données, puis rafraîchissant la vue matérialisée par la suite. Cependant, comme la requête "Refresh Materialized View" prend au moins plusieurs minutes, de telles requêtes s'accumulent dans une file d'attente et elles l'exécutent tous après l'autre.

Malheureusement, dans de tels cas, seule la dernière requête est de toute pertinence; Toutes les requêtes précédentes consomment du temps de traitement en vain pour rafraîchir les données stables. Existe-t-il un moyen d'arrêter déjà d'exécuter des appels pour "rafraîchir la vue matérialisée" lorsqu'un nouvel appel est émis?

Veuillez noter que "Actualiser la vue matérialisée simultanément" a le même comportement, mais ralentit considérablement l'actualisation (de quelques minutes jusqu'à une heure), aggravant ainsi le problème de performance.

Bien sûr, il est possible de tester un verrou existant à la vue de chaque nouvelle requête, il est donc facile d'annuler de nouvelles requêtes; Le problème est que je préférerais annuler les anciennes requêtes et ne garder que le dernier ...

1
wazoox

On dirait que vous souhaitez actualiser les vues matérialisées chaque fois que les données des tables changent. Si vous faites cela avec des vues matérialisées, cela prendra beaucoup de temps et les mises à jour se bloqueront les unes des autres et les interrogations.

Peut-être que vous pouvez construire votre propre "sur commistation de commettre" des vues matérialisées en tant que tables.

Un exemple simple:

À la place de

CREATE MATERIALIZED VIEW sum_eumel AS
SELECT eumel_category,
       sum(eumel_data) AS eumel_sum
FROM eumel
GROUP BY eumel_category;

vous pourriez faire cela:

BEGIN;

CREATE TABLE sum_eumel (
   eumel_category text NOT NULL PRIMARY KEY,
   eumel_sum bigint NOT NULL DEFAULT 0
);

CREATE FUNCTION eumel_trigger() RETURNS trigger
   LANGUAGE plpgsql AS
$$BEGIN
   IF TG_OP IN ('UPDATE', 'DELETE') THEN
      /*
       * Will leave rows with value 0 after the last
       * row for a category has been deleted.
       */
      UPDATE sum_eumel
      SET eumel_sum = eumel_sum - OLD.eumel_data
      WHERE eumel_category = OLD.eumel_category;
   END IF;

   IF TG_OP IN ('INSERT', 'UPDATE') THEN
      INSERT INTO sum_eumel (eumel_category, eumel_sum)
      VALUES (NEW.eumel_category, NEW.eumel_data)
      ON CONFLICT TO UPDATE
      SET eumel_sum = sum_eumel.eumel_sum + EXCLUDED.eumel_data
   END IF;

   IF TG_OP = 'TRUNCATE' THEN
      TRUNCATE sum_eumel;
      RETURN NULL;
   END IF;

   IF TG_OP = 'DELETE' THEN
      RETURN OLD;
   ELSE
      RETURN NEW;
   END IF;
END;$$;

CREATE TRIGGER eumel_dml_trig
   AFTER INSERT OR UPDATE OR DELETE ON eumel
   FOR EACH ROW EXECUTE PROCEDURE eumel_trigger();

CREATE TRIGGER eumel_truncate_trig
   AFTER TRUNCATE ON eumel
   FOR EACH STATEMENT EXECUTE PROCEDURE eumel_trigger();

INSERT INTO sum_eumel
SELECT eumel_category,
       sum(eumel_data) AS eumel_sum
FROM eumel
GROUP BY eumel_category;

COMMIT;
1
Laurenz Albe