Représentations matérialisées
Une représentation matérialisée (MV) est un résultat de requête physiquement stocké (agrégation/projection) qui est mis à jour périodiquement ou en continu et disponible pour des lectures rapides. En fait, ce sont des données « pré-calculées » avec une fraîcheur contrôlée et un coût de lecture.
Principaux objectifs :- Stabiliser la latence des lectures (p95/p99).
- Décharger les tables OLTP « chaudes ».
- Donnez un SLA prévisible pour les analyses, les API et les fiches (recommandations, compteurs, catalogues).
1) Quand utiliser le VM (et quand - non)
Convient :- Requêtes lourdes (join/agg/window) souvent répétées avec un délai de mise à jour valide.
- CQRS/projections de produits : dashboards, catalogues, listes classées, compteurs.
- Lectures multi-régionales : copies « locales » des résultats.
- La pertinence ultra-stricte « par enregistrement » sans logique de compensation → mieux que les index/OLTP + cache/streaming.
- Les invariants transactionnels complexes lors de l'écriture de → MV ne remplacent pas les transactions.
2) MV vs cache vs projection
Cache : « copie de réponse », géré par TTL/invalide au niveau de l'application ; il n'y a pas de schéma.
MV : « copie de données », gérée par la base de données/moteur ; il y a un schéma, des index, une transaction refresh.
Projection (event sourcing/CQRS) : calculée à partir des événements ; il est souvent réalisé comme table + updates incrémentales (c'est-à-dire essentiellement « MV manuel »).
3) Méthodes de mise à jour
3. 1 Lot REFRESH (périodique)
Planificateur (cron/sceduler) : 'REFRESH MATERIALIZED VIEW...'.
Avantages : simple, prévisible, bon marché. Points négatifs : les fenêtres sont insipides.
3. 2 Refresh incrémental
Delts par clé/fenêtre temporelle, upsert's en MV.
Source du changement : CDC (Debezium, replication logique), streaming (Kafka/Flink/Spark), déclencheurs.
Avantages : peu de retard et de coût. Inconvénients : le code et la consistance sont plus complexes.
3. 3 Continu (streaming MV)
Dans les ICE de colonne/streaming : flux/tables matérialisés (ClickHouse/Kafka, Flink SQL, Materialize, BigQuery MV).
Avantages : secondes et plus bas. Inconvénients : nécessite un stream infra et des clés/filigranes clairs.
4) Cohérence et « fraîcheur »
La forte consistance de MV se produit avec le refresh « atomique » (read-switch pour la nouvelle version).
Plus souvent, bounded staleness : « pas plus vieux que Δ t/fenêtre ». Communiquez cela dans les contrats API/UX.
Pour les paiements/invariants stricts, gardez le noyau CP dans OLTP et utilisez MV comme plan de lecture.
5) Simulation et schéma
Rendre le VM étroit comme prévu : une tâche est un VM.
Gardez les clés temporaires (event_time/watermark) et les clés métiers (tenant_id, entity_id).
Index pour filtres/triages fréquents ; Les bases de données à colonnes sont sous les agrégats/scans.
Lot par date/tenant/région pour rapide refresh et retence.
6) Updates incrémentales : modèle upsert-projection
1. Le changement (CDC/événement) arrive.
2. Nous considérons le delta pour la chaîne MV (recompute/merge).
3. 'UPSERT' par clé ('tenant _ id, entity_id, bucket').
4. Nous mettons à jour les métadonnées de fraîcheur.
L'idempotence est obligatoire : la répétition du delta ne doit pas briser le total.
7) Exemples (conceptuels)
PostgreSQL (batterie refresh)
sql
CREATE MATERIALIZED VIEW mv_sales AS
SELECT date_trunc('day', created_at) AS day,
tenant_id,
SUM(amount) AS revenue,
COUNT() AS orders
FROM orders
GROUP BY 1,2;
-- Быстрые чтения
CREATE INDEX ON mv_sales (tenant_id, day);
-- Без блокировок чтения при обновлении
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_sales;
ClickHouse (streaming MV из Kafka)
sql
CREATE TABLE events_kafka (..., ts DateTime, tenant_id String)
ENGINE = Kafka SETTINGS kafka_broker_list='...',
kafka_topic_list='events',
kafka_format='JSONEachRow';
CREATE MATERIALIZED VIEW mv_agg
ENGINE = AggregatingMergeTree()
PARTITION BY toDate(ts)
ORDER BY (tenant_id, toStartOfMinute(ts)) AS
SELECT tenant_id,
toStartOfMinute(ts) AS bucket,
sumState(amount) AS revenue_state
FROM events_kafka
GROUP BY tenant_id, bucket;
BigQuery MV (auto-update)
sql
CREATE MATERIALIZED VIEW dataset.mv_top_products
AS SELECT product_id, SUM(amount) AS revenue
FROM dataset.orders
WHERE _PARTITIONDATE BETWEEN DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY) AND CURRENT_DATE()
GROUP BY product_id;
8) Fraîcheur dans les interfaces/contrats
Retournez 'X-Data-Freshness : <seconds> '/le champ' as _ of '.
Pour les écrans critiques - « bouton de mise à jour » et badge « mis à jour N avec l'arrière ».
Dans l'API, indiquez la fraîcheur SLO (par exemple, p95 ≤ 60s).
9) Multi-tenants et régions
La clé 'tenant _ id'dans MV est obligatoire.
Fairness : quotas de refresh/stream par locataire ; Sheduling de grands MV la nuit per tenant.
Résidence : MV vit dans la même région que les données primaires ; la région croisée n'est que des agrégats.
10) Observabilité
Métriques :- `freshness_age_ms` (p50/p95/p99), `refresh_latency_ms`, `rows_processed/s`, `refresh_errors`.
- Taille des VM/lots, coûts de stockage.
- Pour le streaming : lag du connecteur, « eau » (watermark), part des événements de late.
- Теги: `mv_name`, `tenant_id`, `partition`, `refresh_id`, `delta_size`.
- Rapports sur les « recalculs » et les fautes de cause (schema mismatch, timeout).
11) Test et chaos
Correctness : comparaison de la source MV vs sur les sous-échantillons ; montants de contrôle.
Freshness under load : charge d'enregistrement + garantie de fraîcheur SLO.
Schema evolution : ajout/renommage de champs, « chute » du connecteur CDC.
Late/Out-of-order : répliques d'événements, changement de filigrane.
Idempotence : Livraison répétée des deltas/trampolines.
12) Rétention et coût
Gardez seulement les fenêtres que vous voulez (par exemple, 90 jours) ; Archiver les anciens lots.
Aspiration régulière/merge (par moteur).
Réduisez le MV à des API/pages spécifiques en évitant le « monstre universel ».
13) Sécurité et conformité
Héritez des politiques d'accès source (RLS/ACL) - ne distribuez pas de MV plus large que les tables sources.
Masquer PII lors de la construction de MV, en particulier pour les analystes/logs.
Audit des refresh/redraves.
14) Erreurs typiques
« Un énorme MV pour tout » → cher refresh et faible isolation.
L'absence d'index/lots → p99 saute, refresh étouffe le cluster.
Recalculer complètement au lieu des deltas là où vous pouvez incrémentalement.
La fraîcheur non déclarée dans l'API/UX → les revendications des utilisateurs sur les données « obsolètes ».
Ignorer schema evolution/CDC erreurs → perte de cohérence.
Tentative de remplacer MV par des transactions : MV - sur la lecture, pas sur les opérations d'écriture strictes.
15) Recettes rapides
Produit Dashboard : MV en baquets minutes/heures, refresh en horaire + on-demand pour VIP, p95 fraîcheur ≤ 60 s.
Catalogue/recherche : projection incrémentale à partir de CDC (upsert), index par filtre, lag ≤ 5-15 s.
Fin-reporting : paquet MV avec atomic 'REFRESH CONCURRENTLY', les sommes de contrôle, « as_of » dans les réponses.
Global SaaS : MV régionaux, l'agrégation cross-régionale est asynchrone.
16) Chèque-liste avant la vente
- Défini par le SLA de fraîcheur (Δt/p95) et il est reflété dans l'API/UX.
- Mode sélectionné : batch refresh/incrémental/streaming ; les sources (CDC/événements) sont décrites.
- MV est conçu « par tâche », il y a des index et des lots, le stockage est limité à une fenêtre.
- L'idempotence upsert/agrégats est confirmée par des tests ; traitement late/out-of-order.
- Observabilité : métriques de fraîcheur/laga, alertes, tracing refresh.
- Pleybooks : recalculer les lots, redrave après défaillance du connecteur, évolution du schéma.
- L'accès et le PII correspondent à l'origine ; l'audit est inclus.
- Coût sous contrôle : rétention, compression, temps de fenêtre refresh.
- Documentation : que dans MV « vérité », ce qui est la couche dérivée, les attentes de l'entreprise.
Conclusion
Les représentations matérialisées sont un compromis d'ingénierie entre la vitesse de lecture et la pertinence. Avec un SLA clair de fraîcheur, un schéma correct, une mise à jour incrémentale et une télémétrie normale, les MV transforment les requêtes lourdes en millisecondes prévisibles - sans sacrifier la fiabilité et le contrôle des coûts.