Stockage des séries chronologiques
1) Pourquoi une architecture séparée pour les séries chronologiques
Les séries temporelles (time series) sont des séquences de paires (timestamp, value) marquées (labels) qui se caractérisent par :- Taux d'enregistrement élevé (ingest) et fréquence.
- Lecture par plage de temps (scan + agrégats/fonctions de fenêtre).
- Une cardinalité explosive due à des combinaisons d'étiquettes.
- Par la nécessité du rétentionnement (limitation de la durée de conservation) et du downsampling (compression dans le temps).
- D'où le modèle de stockage spécial, les formats de compression et les protocoles de requête.
2) Modèle de données et contrats métriques
2. 1 Nommage et balises
metric_name : verbe/nom au singulier (« http _ requests _ total », « cpu _ usage _ secondes _ total »).
labels : clés-attributs ('job', 'instance', 'dc', 'pod', 'status', 'method').
Invariants : ne pas changer la sémantique du nom, ajouter des versions ('metric _ v2') en cas de modifications incompatibles.
2. 2 Types de rangées
Gauge (instantané), Counter (total croissant), Histogramme/Summary (distributions/quantifies), Event/Span (points de trace).
Pour les finances/densités - fixez les unités de mesure et l'agrégabilité (résumée/moyenne).
2. 3 La politique de la rétention et des rollaps
Détails chauds (secondes/1-10 min) → unités chaudes (5m/1h) → froides (1d/1w).
Pour counter - stocker rate/deriv agrégats.
3) Chemin d'enregistrement : réception, tampon, CD
3. 1 Ingest-Pipline
Scrape (pull, Prometheus) ou push (OTLP/StatsD/Graphite), souvent via gateway/agent.
Mise en tampon dans WAL (write-ahead log), puis compaction en segments/blocs (architecture LSM).
Batching et triage temporel augmentent la compression et la vitesse.
3. 2 Traitement out-of-order et prises
Fenêtre de tolérance (fenêtre lateness, par exemple 5-15 min) + stratégie : 'drop | upsert | keep-last'.
Déduplication par '(series_id, timestamp)' avec la versionalité ou « le dernier enregistrement gagne ».
3. 3 Compression
Delta-of-delta pour les étiquettes de temps, Gorilla/XOR pour float, RLE et varint pour les étiquettes entières, dictionnaire pour les étiquettes.
La taille optimale du bloc (« chanca ») des points 1-8K est un compromis entre l'IOPS et le CPU.
4) Schémas de stockage : TSDB vs SQL/colonnettes
4. 1 TSDB spécialisé
Prometheus (local, courte rétention, BouQL, remote_write).
VictoriaMetrics/M3/InfluxDB - mise à l'échelle horizontale, longue durée, lecture à distance.
Les formats de bloc sont optimisés pour les agrégations d'appel d'offres range scan +.
4. 2 Moteurs relationnels/à colonnes
TimescaleDB (PostgreSQL) : hypertablis, cuves par temps/espace, aggregates continues.
ClickHouse : MergeTree/TTL/représentations matérialisées, excellente compression et agrégation dans le temps.
La sélection se fait en fonction de l'écosystème des requêtes (SQL vs BouQL), des exigences de join/BI et des compétences opérationnelles de l'équipe.
5) Schéma et exemples
5. 1 TimescaleDB : Hypertablique + continuous aggregate
sql
CREATE TABLE metrics_cpu(
ts timestamptz NOT NULL,
host text NOT NULL,
dc text NOT NULL,
usage double precision NOT NULL,
PRIMARY KEY (ts, host, dc)
);
SELECT create_hypertable('metrics_cpu', by_range('ts'), chunk_time_interval => interval '1 day');
-- Continuous unit (5 minutes)
CREATE MATERIALIZED VIEW cpu_5m
WITH (timescaledb. continuous) AS
SELECT time_bucket('5 minutes', ts) AS ts5m, host, dc, avg(usage) AS avg_usage
FROM metrics_cpu GROUP BY 1,2,3;
-- Politicians
SELECT add_retention_policy('metrics_cpu', INTERVAL '14 days');
SELECT add_retention_policy('cpu_5m', INTERVAL '180 days');
5. 2 ClickHouse : Stockage agrégateur
sql
CREATE TABLE metrics_cpu (
ts DateTime,
host LowCardinality(String),
dc LowCardinality(String),
usage Float32
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(ts)
ORDER BY (host, dc, ts)
TTL ts + INTERVAL 14 DAY
SETTINGS index_granularity = 8192;
-- Rollup in hourly detail
CREATE MATERIALIZED VIEW cpu_1h
ENGINE = SummingMergeTree()
PARTITION BY toYYYYMM(ts)
ORDER BY (host, dc, ts)
POPULATE AS
SELECT toStartOfHour(ts) AS ts, host, dc, avg(usage) AS usage
FROM metrics_cpu GROUP BY ts, host, dc;
5. 3 Prometheus/VictoriaMetrics: remote_write
yaml global:
scrape_interval: 15s remote_write:
- url: http://vminsert:8480/insert/0/prometheus/api/v1/write
6) Cardinalité : comment ne pas « faire sauter » le coffre
6. 1 Règles
Limitez le label cardinality (nombre de valeurs uniques). N'incluez pas 'user _ id', 'request _ id', 'trace _ id'.
Normaliser les balises « à plusieurs valeurs » (catégories → codes).
Utilisez les types LowCardinality (en CH), les dictionnaires/arbres d'étiquette (en TSDB).
6. 2 Contrôles et alertes
Métriques : 'series _ count', 'label _ values {label}', top-N de séries « chères ».
Les stratégies de refus d'écriture lorsque la limite de cardinalité per tenant/job est dépassée.
6. 3 Histoires/histogrammes
Pour la haute-cardinalité, il est préférable de stocker les agrégats (histogramme buckets) et pre-rollup ; quantifié pour calculer en ligne sur les agrégats.
7) Retenshn, downsampling et tiered-storage
7. 1 Politiques
Hot : 3-30 jours de détails secondes/minutes.
Warm : 90-365 jours 5m/1h agrégats.
Cold : années d'agrégats diurnes, archive dans le stockage d'objets (S3/Glacier) avec Parquet.
7. 2 Techniques
Continuous aggregates (Timescale), représentations matérialisées (CH), reprise + rollup tasks (Victoria/M3/Influx).
Tiered storage : « blocs chauds » localement, « froids » dans un objet avec cache local.
8) Demandes et langues
8. 1 BouQL (exemple)
promql rate(http_requests_total{job="api",status=~"5.."}[5m])
Nous recherchons un taux d'erreur 5xx par API.
8. 2 agrégats SQL par fenêtre
sql
SELECT time_bucket('1h', ts) AS hour,
dc, avg(usage) AS avg, max(usage) AS pmax
FROM metrics_cpu
WHERE ts >= now() - interval '24 hours'
GROUP BY 1,2 ORDER BY 1;
8. 3 Anomalies (croquis)
Z-score/ESD sur les statistiques des fenêtres, décomposition STL de la saisonnalité ; stocker les résultats dans une rangée distincte de 'anomaly = 1/0'.
9) Intégrations et protocoles
OTLP (OpenTelemetry) : les actes de naissance/trejsy/logi, les exportateurs sur les agents (otel-collector) → TsDB/klikkhaous/ob'ektnoe.
StatsD/Graphite : compteurs/minuteurs simples ; proxy sur edge, puis conversion en format unique.
Kafka/NATS : tampon pour les éclats d'ingest, replayer pour le backfill ; les consumers écrivent avec des battes.
text kafka(topic=metrics) -> stream processor (normalize/tags) -> CH INSERT INTO metrics_cpu FORMAT RowBinary
10) Accessibilité, HA et fédération
Replica/HA-paires TSDB ou fédération Prometheus (niveau région → global).
Remote read/write pour le stockage à long terme et les dashboards centralisés.
Shard-by-label/time : distribution uniforme d'ingest, localité par 'dc/tenant'.
11) L'observabilité de l'entrepôt lui-même
11. 1 Métriques
Ingest: `samples/sec`, `append_latency`, `wal_fsync_ms`.
Хранение: `blocks_count`, `compaction_queue_len`, `chunk_compression_ratio`.
Запросы: `query_qps`, `scan_bytes`, `p95/p99_latency`, `alloc_bytes`.
Cardinalité : 'series _ count', top-labels.
11. 2 SLO
« p99 latency pour la gamme 1h ≤ 200 ms à QPS≤500 ».
«Ingest-drop ≤ 0. 01 % pour burst jusqu'à X samples/sec".
«Compaction backlog < 10 min».
11. 3 Alert
Croissance 'series _ count'> %/heure.
File d'attente compaction/flush> seuil.
Доля out-of-order > N%, dedup/late-drops.
12) Sécurité et multi-ténacité
Isolation par 'tenant' (label dans les clés, tables/bases séparées, quotas).
Assainissement des étiquettes (interdiction des PII), contrôle des dimensions/valeurs.
Cryptage « au repos » et dans les transports, audit de l'accès aux mesures « sensibles ».
13) Pratiques opérationnelles
Échauffement et démarrage à froid : pin des blocs « chauds » dans le cache, prefetch des dernières N heures.
Backfill : piplines individuelles à faible priorité, ne pas mélanger avec en ligne.
Versioner le schéma : Migration avec écriture parallèle (dual-write) et pull subséquent.
Budget de stockage : contrôle de 'cost _ per _ TB _ month' + forecast croissance cardinalité.
14) Anti-modèles
Tags à haute cardinalité (user_id, uuid) → explosion de rangées.
Les rangs « éternels » sans retentissement → une croissance incontrôlée.
L'enregistrement sans batch/tri → une mauvaise compression et une tempête IOPS.
Mélange OLTP et scans longs sur un seul pool de disques.
L'absence de politique out-of-order → dupliqué et gonflé.
Les histogrammes avec des centaines de réservoirs → un coût × 10 sans avantages.
15) Chèque de mise en œuvre
- Identifier les métriques, leurs types et leurs unités ; fixez le contrat des noms/labels.
- Sélectionnez le moteur (TSDB vs SQL/colonneur) et le langage de requête (BouQL/SQL).
- Concevoir Retensh/rollap (hot/warm/cold) et ILM-taski.
- Configurez ingest : WAL/Batchi/Trier, fenêtres out-of-order.
- Inclure la compression (delta-of-delta/XOR/RLE), cuves optimales.
- Contrôler la cardinalité : quotas, alertes, politiques d'abandon.
- Configurez SUR/Fédération et remote-write/read.
- Dashboards SLO et métriques de stockage (ingest/query/storage).
- Politiques de sécurité/confinement et absence de PII dans les labels.
- « game day » régulier : backfill, perte de nœud, sursaut d'ingest.
16) FAQ
Q : Que choisir pour surveiller les infrastructures : Prometheus ou ClickHouse/Timescale ?
R : Pour la surveillance native et BouQL - Prometheus + stockage à long terme (Victoria/M3). Pour les scripts BI/entrepôt et SQL - Timescale/ClickHouse.
Q : Comment stocker quantili sans résumé lourd ?
R : Utilisez l'histogramme avec des réservoirs bien rangés et calculez quantiou lorsque demandé ; ou t-digest/CKMS dans les unités.
Q : Comment faire avec out-of-order ?
A : Entrez une fenêtre de tolérance (5-15 min) et une politique de dedup déterministe ; pour la télémétrie à partir de mobile/edge - la fenêtre est plus large.
Q : Quand avez-vous besoin d'un rollup ?
A : Toujours à ретеншне> 30-90 jours : les groupes de machines réduisent le montant ×10-100 et accélèrent à l'analyste.
Q : Est-il possible de mélanger logs et métriques ?
R : Stockez séparément (les formats/demandes sont différents). Pour la corrélation, utilisez Exemplar/TraceID et dashboards, mais ne mettez pas tout dans le même tableau.
17) Résultats
Le stockage efficace des séries chronologiques est un contrat métrique + discipline des étiquettes, l'ingest compétent (WAL/compaction), la compression et les politiques de rétention/rouleaux. Ajoutez le contrôle de la cardinalité, la NA/fédération et l'observabilité de l'acier lui-même - et vous obtiendrez un p95 prévisible, un coût raisonnable pour les mois-années et une résistance aux surtensions.