Tous les produits
Search
Centre de documentation

Hologres:Comprehensive guide to materialized views

Dernière mise à jour :Aug 11, 2026

Les vues matérialisées en temps réel de Hologres préagrègent les données de la table de base et stockent les résultats. Les requêtes lisent directement la vue au lieu de recalculer l'agrégation à la volée, ce qui réduit la latence et la charge de calcul.

Contrairement aux vues matérialisées traditionnelles qui nécessitent une actualisation manuelle, celles de Hologres se mettent à jour automatiquement. Les modifications apportées à la table de base sont immédiatement répercutées dans la vue : aucune tâche d'actualisation planifiée n'est nécessaire.

Materialized view structure

La table qui reçoit les écritures en temps réel est appelée table de base. Toutes les opérations INSERT, UPDATE et DELETE ciblent la table de base. La vue matérialisée est définie par des règles d'agrégation sur la table de base et reflète les insertions en temps réel. La prise en charge de la synchronisation des mises à jour et des suppressions sera ajoutée dans une prochaine version.

Quand utiliser les vues matérialisées

Les vues matérialisées constituent une solution adaptée lorsque :

  • Les requêtes agrègent régulièrement de grands volumes de données brutes (tableaux de bord, rapports, analyses).

  • Le résultat d'agrégation est consulté beaucoup plus fréquemment que la table de base n'est mise à jour.

  • La latence des requêtes sur la table de base représente un goulot d'étranglement, même avec des index.

Évitez les vues matérialisées si le résultat d'agrégation contient à peu près autant de lignes que la table de base (faible taux de compression) ou si le modèle de requête change fréquemment, rendant la clé GROUP BY fixe peu pratique.

Fonctions d'agrégation prises en charge

Function Notes
SUM
COUNT
AVG
MIN
MAX
RB_BUILD_CARDINALITY_AGG Type de données BIGINT uniquement. Nécessite l'extension roaringbitmap.

Prérequis

Avant de commencer, assurez-vous de disposer des éléments suivants :

  • Une instance Hologres avec un accès en écriture

  • L'extension roaringbitmap créée (requise uniquement pour RB_BUILD_CARDINALITY_AGG)

Créer une vue matérialisée en temps réel

Créez la table de base et la vue matérialisée dans une seule transaction. La table de base doit avoir le type de mutation appendonly.

BEGIN;
CREATE TABLE base_sales(
  day  text        NOT NULL,
  hour int,
  ts   timestamptz,
  amount float,
  pk   text        NOT NULL PRIMARY KEY
);
CALL SET_TABLE_PROPERTY('base_sales', 'mutate_type', 'appendonly');

-- After dropping the materialized view, remove the appendonly property if needed:
-- CALL SET_TABLE_PROPERTY('base_sales', 'mutate_type', 'none');

CREATE MATERIALIZED VIEW mv_sales AS
  SELECT
    day,
    hour,
    avg(amount) AS amount_avg
  FROM base_sales
  GROUP BY day, hour;

COMMIT;

Insérez des données dans la table de base. La vue reflète immédiatement les nouvelles données.

INSERT INTO base_sales VALUES (to_char(now(),'YYYYMMDD'), '12', now(), 100, 'pk1');
INSERT INTO base_sales VALUES (to_char(now(),'YYYYMMDD'), '12', now(), 200, 'pk2');
INSERT INTO base_sales VALUES (to_char(now(),'YYYYMMDD'), '12', now(), 300, 'pk3');

Interroger une vue matérialisée

Interrogez directement la vue à l'aide du SQL standard.

SELECT * FROM mv_sales WHERE day = to_char(now(),'YYYYMMDD') AND hour = 12;

Vous pouvez également interroger la table de base et laisser le routage intelligent rediriger automatiquement vers la vue. Consultez Routage intelligent pour les vues matérialisées.

Créer une vue matérialisée pour une table partitionnée

Lorsque la table de base est partitionnée, la clé GROUP BY de la vue matérialisée doit inclure la colonne de clé de partition. Créez la vue matérialisée sur la table parente, et non sur les tables enfants individuelles.

BEGIN;
CREATE TABLE base_sales_p(
  day    text NOT NULL,
  hour   int,
  ts     timestamptz,
  amount float,
  pk     text NOT NULL,
  PRIMARY KEY (day, pk)
) PARTITION BY LIST(day);
CALL SET_TABLE_PROPERTY('base_sales_p', 'mutate_type', 'appendonly');

-- day is the partition key and must appear in the GROUP BY clause
CREATE MATERIALIZED VIEW mv_sales_p AS
  SELECT
    day,
    hour,
    avg(amount) AS amount_avg
  FROM base_sales_p
  GROUP BY day, hour;
COMMIT;

-- Use CREATE TABLE PARTITION OF to add partitions (ATTACH PARTITION is not supported)
CREATE TABLE base_sales_20220101 PARTITION OF base_sales_p FOR VALUES IN ('20220101');

Autres opérations

Vérifier la taille de stockage

Vérifiez la taille de stockage d'une seule vue matérialisée :

SELECT pg_relation_size('mv_sales');

Listez toutes les vues matérialisées triées par taille de stockage :

SELECT schemaname || '.' || matviewname AS mv_full_name,
       pg_size_pretty(pg_relation_size('"' || schemaname || '"."' || matviewname || '"')) AS mv_size,
       pg_relation_size('"' || schemaname || '"."' || matviewname || '"') AS order_size
FROM pg_matviews
ORDER BY order_size DESC;

Supprimer une vue matérialisée

DROP MATERIALIZED VIEW mv_sales;

Calcul précis des visiteurs uniques (UV) avec RoaringBitmap

Le calcul précis des visiteurs uniques (UV), qui consiste à compter les ID utilisateur distincts, est coûteux en calcul et constitue un goulot d'étranglement courant en termes de performances. RB_BUILD_CARDINALITY_AGG préagrège les champs d'ID métier BIGINT dans un RoaringBitmap au sein de la vue matérialisée, permettant une déduplication en temps réel. Seuls les champs BIGINT sont pris en charge pour cette agrégation.

-- The roaringbitmap extension must be created before use
CREATE EXTENSION IF NOT EXISTS roaringbitmap;

BEGIN;
CREATE TABLE base_sales_r(
  day    text        NOT NULL,
  hour   int,
  ts     timestamptz,
  amount float,
  userid bigint,
  pk     text        NOT NULL PRIMARY KEY
);
CALL SET_TABLE_PROPERTY('base_sales_r', 'mutate_type', 'appendonly');

CREATE MATERIALIZED VIEW mv_sales_r AS
  SELECT
    day,
    hour,
    avg(amount)                      AS amount_avg,
    rb_build_cardinality_agg(userid) AS user_count
  FROM base_sales_r
  GROUP BY day, hour;

COMMIT;

INSERT INTO base_sales_r VALUES (to_char(now(),'YYYYMMDD'), '12', now(), 100, 1, 'pk1');
INSERT INTO base_sales_r VALUES (to_char(now(),'YYYYMMDD'), '12', now(), 200, 2, 'pk2');
INSERT INTO base_sales_r VALUES (to_char(now(),'YYYYMMDD'), '12', now(), 300, 3, 'pk3');

-- user_count holds the count of distinct userid values for that day+hour
SELECT user_count AS UV FROM mv_sales_r WHERE day = to_char(now(),'YYYYMMDD') AND hour = 12;

Agrégation multidimensionnelle avec des fonctions partielles

Pourquoi AVG standard échoue entre les dimensions

Une vue matérialisée stocke des résultats préagrégés. Si vous définissez mv_sales avec avg(amount) groupé par (day, hour), l'interrogation portant uniquement sur la dimension day produit des résultats incorrects : la moyenne des moyennes n'est pas égale à la moyenne totale.

Compte tenu de ces données de base :

Day Hour Amount PK
20210101 12 2 pk1
20210101 12 4 pk2
20210101 13 6 pk3

Une requête directe sur la vue renvoie :

postgres=> SELECT * FROM mv_sales;
    day    | hour | amount_avg
-----------+------+------------
  20210101 |   12 |     3
  20210101 |   13 |     6

La réagrégation par day donne un résultat incorrect :

postgres=> SELECT day, avg(amount_avg) FROM mv_sales GROUP BY day;
    day    |  avg
-----------+------
  20210101 |  4.5    -- incorrect: true average is 4

Utiliser l'état d'agrégation intermédiaire

Au lieu de stocker la moyenne finale, stockez l'état d'agrégation intermédiaire à l'aide de avg_partial. Lors de l'interrogation selon une dimension différente, finalisez le résultat avec avg_final.

BEGIN;
CREATE TABLE base_sales(
  day    text        NOT NULL,
  hour   int,
  ts     timestamptz,
  amount float,
  pk     text        NOT NULL PRIMARY KEY
);
CALL SET_TABLE_PROPERTY('base_sales', 'mutate_type', 'appendonly');

CREATE MATERIALIZED VIEW mv_sales_partial AS
  SELECT
    day,
    hour,
    avg(amount)         AS avg,
    avg_partial(amount) AS amt_avg_partial   -- stores intermediate state
  FROM base_sales
  GROUP BY day, hour;

COMMIT;

Interrogez selon une autre dimension d'agrégation à l'aide de avg_final :

postgres=> SELECT day, avg(avg) AS avg_avg, avg_final(amt_avg_partial) AS real_avg
           FROM mv_sales_partial
           GROUP BY day;
    day    | avg_avg | real_avg
-----------+---------+----------
  20210101 |     4.5 |        4   -- real_avg is correct

Référence des fonctions partielles et finales

Standard function Partial function Final function
AVG AVG_PARTIAL AVG_FINAL
RB_BUILD_CARDINALITY_AGG RB_BUILD_AGG RB_OR_CARDINALITY_AGG

Routage intelligent pour les vues matérialisées

Interrogez directement la table de base : l'optimiseur réécrit automatiquement la requête pour utiliser une vue matérialisée correspondante. Aucune modification de la syntaxe de la requête n'est requise.

L'optimiseur sélectionne une vue lorsque toutes les conditions suivantes sont remplies :

  1. La vue contient toutes les colonnes référencées dans la requête (ou les colonnes à partir desquelles les valeurs interrogées peuvent être dérivées).

  2. Les colonnes GROUP BY de la vue incluent toutes les colonnes GROUP BY de la requête d'origine.

  3. Si plusieurs vues sont éligibles, l'optimiseur choisit celle qui possède le moins de colonnes GROUP BY.

Fonctions d'agrégation prises en charge pour le routage intelligent : SUM, COUNT, MIN, MAX.

AVG et RB_BUILD_CARDINALITY_AGG ne prennent pas en charge le routage intelligent.

Considérations relatives au TTL

Les données sous-jacentes d'une vue matérialisée partagent la même durée de vie (TTL) que sa table de base. Ne définissez pas de TTL séparé sur la vue matérialisée : des valeurs de TTL incohérentes entraînent une incohérence des données entre la vue et la table de base.

Lorsque le TTL de la table de base expire et que les données sont récupérées, une requête sur la table de base et une requête sur la vue peuvent renvoyer des résultats différents. Par exemple :

Requête sur la table de base après expiration du TTL :

postgres=> SELECT day, hour, avg(amount) AS amount_avg FROM base_sales GROUP BY day, hour;
    day    | hour | amount_avg
-----------+------+------------
  20210101 |   12 |     4
  20210101 |   13 |     6

Requête sur la vue — les données récupérées avaient déjà été matérialisées et restent dans la vue :

postgres=> SELECT * FROM mv_sales;
    day    | hour | amount_avg
-----------+------+------------
  20210101 |   12 |     3       -- stale; reflects pre-reclamation state
  20210101 |   13 |     6

Pour éviter cette incohérence, utilisez l'une des approches suivantes :

  • Ne définissez pas de TTL sur la table de base.

  • Si un TTL est requis, incluez un champ temporel dans la clé GROUP BY de la vue. Lors de l'interrogation, évitez les plages horaires proches de la limite d'expiration du TTL.

  • Utilisez une table partitionnée sans TTL. Récupérez les anciennes données en supprimant les partitions enfants.

Limitations

Contraintes de création

  • Les vues matérialisées doivent être créées dans la même transaction que la table de base. La création asynchrone n'est pas prise en charge.

  • Une vue matérialisée ne peut faire référence qu'à une seule table. Les expressions de table communes (CTE), les jointures multitable, les sous-requêtes ainsi que les clauses WHERE, ORDER BY, LIMIT et HAVING ne sont pas prises en charge.

  • La clé GROUP BY et les valeurs d'agrégat ne prennent pas en charge les expressions. Par exemple, SUM(CASE WHEN cond THEN a ELSE b END), SUM(col1 + col2) et GROUP BY date_trunc('hour', ts) ne sont pas pris en charge.

  • Maximum de 10 vues matérialisées par table de base. La consommation de ressources augmente proportionnellement au nombre de vues.

  • Pour les tables partitionnées : la clé GROUP BY doit inclure la colonne de clé de partition ; la vue doit être créée sur la table parente.

  • Pour les tables partitionnées : ATTACH PARTITION n'est pas pris en charge pour l'ajout de partitions ; utilisez plutôt CREATE TABLE PARTITION OF.

Contraintes d'exploitation

  • Les opérations DELETE et UPDATE sur une table de base ne sont pas prises en charge. Définissez la propriété appendonly sur la table de base. Toute tentative de DELETE ou UPDATE renvoie l'erreur Table XXX is append-only.

  • Lors de l'écriture en temps réel dans la table de base à l'aide de Flink, définissez mutateType sur InsertOrIgnore.

  • DROP COLUMN sur une table de base possédant une vue matérialisée n'est pas pris en charge.

  • Ne définissez pas manuellement de TTL sur une vue matérialisée. La vue hérite du TTL de la table de base. La définition d'un TTL différent entraîne une incohérence des données.

Bonnes pratiques

  • Alignez la clé GROUP BY sur la clé de distribution. Définissez la clé GROUP BY de la vue matérialisée de manière à ce qu'elle corresponde à la clé de distribution de la table de base. Cela améliore à la fois le taux de compression des données et les performances des requêtes.

  • Placez les colonnes de filtre en premier dans la clé GROUP BY. Placez les colonnes fréquemment utilisées dans les filtres WHERE au début de la clé GROUP BY. Cela suit le principe de correspondance la plus à gauche de la clé de regroupement et permet un élagage plus efficace.