Przechowywanie szeregów czasowych
1) Dlaczego oddzielna architektura dla serii czasowych
Serie czasowe to sekwencje par (znacznik czasowy, wartość) z tagami (etykiety), które charakteryzują się:- Wysoka prędkość nagrywania (połknięcie) i częstotliwość.
- Odczytuje według zakresów czasu (skanowanie + agregaty/funkcje okna).
- Wybuchowa kardynalność z powodu kombinacji znaczników.
- Konieczność zatrzymania (ograniczenia trwałości) i obniżenia (kompresja czasu).
- Stąd - specjalny model pamięci masowej, formaty kompresji i protokoły żądań.
2) Model danych i metryki kontraktowe
2. 1 Nazwy i tagi
metric_name: pojedynczy czasownik/rzeczownik ('http _ requests _ total', 'cpu _ usage _ seconds _ total').
etykiety: klucze atrybutów ('job', 'instance', 'dc',' pod ',' status ',' method ').
Niezmienne: nie zmieniaj semantyki nazwy, dodaj wersje ('metric _ v2') z niezgodnymi zmianami.
2. 2 Rodzaje rzędów
Skrajnia, licznik, histogram/podsumowanie, zdarzenie/rozpiętość.
Dla finansów/gęstości - Ustalanie jednostek i agregacji (sumowane/uśrednione).
2. 3 Polityka zatrzymywania i Rollup
Gorący szczegół (sekundy/1-10 min) → ciepłe jednostki (5m/1h) → zimno (1d/1w).
Dla licznika - szybkość przechowywania/agregaty deriv.
3) Ścieżka nagrywania: odbiór, bufor, kompaktowy
3. 1 Rurociąg połykający
Scrape (pull, Prometheus) lub push (OTLP/StatsD/Graphite), często za pośrednictwem bramy/agenta.
Buforowanie w log WAL (write-ahead), a następnie kompresowanie do segmentów/bloków (architektura podobna do LSM).
Sortowanie partii i czasu zwiększa kompresję i szybkość.
3. 2 Obsługa out-of-order i przyjmuje
Okno tolerancji (okno opóźnienia, na przykład 5-15 min) + zasady: „upsert | upsert | keep-last”.
Deduplikacja przez '(series_id, timestamp)' z wersją lub „wygrywa ostatni rekord”.
3. 3 Kompresja
Delta-of-delta for timestamps, Gorilla/XOR for float, RLE i varint for integers, słownik dla tagów.
Optymalny rozmiar bloku („kawałek”) punktów 1-8K jest kompromisem pomiędzy IOPS i procesorem.
4) Systemy przechowywania: TSDB vs SQL/kolumny
4. 1 Dedykowany TSDB
Prometeusz (lokalna, krótka retencja, PromQL, remote_write).
VictoriaMetrics/M3/InfluxDB - skalowanie poziome, długa retencja, zdalny odczyt.
Formaty bloków są zoptymalizowane pod kątem skanowania zakresu + agregacji przetargowych.
4. 2 Silniki relacyjne/kolumnowe
TimescaleDB (PostgreSQL): nadciśnienia, kawałki według czasu/przestrzeni, kruszywa ciągłe.
ClickHouse: MergeTree/TTL/zmaterializowane widoki, doskonała kompresja i agregacja czasu.
Wybór - przez ekosystem zapytania (SQL vs PromQL), przyłączyć/BI wymagania i umiejętności operacyjne zespołu.
5) Schemat i przykłady
5. 1 TimescaleDB: nadmiar + kruszywo ciągłe
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: agregowanie pamięci
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 Prometeusz/VictoriaMetrics: remote_write
yaml global:
scrape_interval: 15s remote_write:
- url: http://vminsert:8480/insert/0/prometheus/api/v1/write
6) Kardynalność: jak nie „wysadzić” magazynu
6. 1 Zasady
Kardynalność etykiety limitowanej (liczba unikalnych wartości). Nie włączaj 'user _ id',' request _ id', 'trace _ id'.
Normalizacja „multi-valued” tagi (kategorie → kody).
Użyj typów LowCardinality (w CH), słowników/etykiet drzew (w TSDB).
6. 2 Kontrole i wpisy
Mierniki: 'series _ count',' label _ values {label} ', top-N' drogie 'wiersze.
Zapisz zasady niepowodzenia, gdy limit kardynalności zostanie przekroczony na najemcę/zadanie.
6. 3 Historie/Histogramy
Dla wysokiej kardynalności, lepiej jest przechowywać kruszywa (wiadra histogramowe) i pre-rollup; kwantyle do obliczania online na agregatach.
7) Zatrzymywanie, obniżanie i magazynowanie wielopoziomowe
7. 1 Polityka
Gorący: 3-30 dni szczegółów sekundy/minuty.
Ciepło: 90-365 dni kruszywa 5m/1h.
Zimno: lata agregatów dziennych, archiwum w magazynie obiektów (S3/Glacier) z parkietem.
7. 2 Technicy
Ciągłe agregaty (skala czasowa), zmaterializowane widoki (CH), zadania retencji + rollup (Victoria/M3/Influence).
Magazyn wielopoziomowy: „gorące bloki” lokalnie, „zimno” w obiekcie z lokalnym buforem.
8) Zapytania i języki
8. 1 PromQL (przykład)
promql rate(http_requests_total{job="api",status=~"5.."}[5m])
Szukasz błędu 5xx API.
8. 2 agregaty SQL według okien
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 Anomalie (szkic)
Z-score/ESD według statystyk okien, STL rozkład sezonowości; przechowywać wyniki w oddzielnym wierszu 'anomalia = 1/0'.
9) Integracja i protokoły
OTLP (OpenTelemetry): mierniki/ścieżki/dzienniki, eksporterzy na agentach (otel-collector) → TSDB/clickhouse/object.
StatsD/Graphite: proste liczniki/zegary; proxy to edge, a następnie konwersja do jednego formatu.
Kafka/NATS: bufor do wybuchów, replika do zasypki; konsumenci piszą batchami.
text kafka(topic=metrics) -> stream processor (normalize/tags) -> CH INSERT INTO metrics_cpu FORMAT RowBinary
10) Dostępność, HA i Federacja
Replika/TSDB HA par lub Prometheus federacji (region → poziom globalny).
Zdalny odczyt/zapis do długotrwałego przechowywania i scentralizowanych desek rozdzielczych.
Shard-by-label/time: jednolity rozkład spożycia, lokalizacja przez 'dc/najemca'.
11) W przedmiocie obserwowalności samego składowania
11. 1 Metryka
Połknięcie: 'próbki/sec', 'append _ latency', 'wal _ fsync _ ms'.
Мранений: 'blocks _ count',' compaction _ queue _ len ',' chunk _ compression _ ratio '.
Саброса: 'query _ qps', 'scan _ bytes', 'p95/p99 _ latency', 'alloc _ bytes'.
Kardynalność: 'series _ count', najwyższe etykiety.
11. 2 SLO
„p99 opóźnienia dla zakresu 1h ≤ 200ms przy QPS ≤ 500”.
"Kropla do spożycia ≤ 0. 01% przy wybuchu przed próbkami X/s"
„Zaległości zagęszczania <10 min”.
11. 3 Wpisy
Wzrost 'series _ count'> Y %/godz.
Kolejka kompresji/spłukiwanie> próg.
Дола-of-order> N%, dedup/late-drops.
12) Bezpieczeństwo i wielopoziomowość
Izolacja przez „najemcę” (etykieta w klawiszach, poszczególne tabele/bazy danych, kwoty).
Oczyszczanie etykiet (zakaz PII), kontrola wielkości/wartości.
Szyfrowanie „w spoczynku” i w transporcie, audyt dostępu do „wrażliwych” mierników.
13) Praktyki operacyjne
Rozgrzewka i zimny start: pin „gorących” bloków w pamięci podręcznej, prefetch ostatnich godzin N.
Zasypka: poszczególne rurociągi o niskim priorytecie, nie mieszać z online.
Wersioning schematu: migracje z równoległym zapisem (dual-write) i kolejnym przełącznikiem.
Budżet na składowanie: kontrola 'cost _ per _ TB _ month' + prognoza wzrostu kardynalności.
14) Anty-wzory
Tagi o wysokiej kardynalności (user_id, uuid) → eksplozja rzędów.
„Wieczne” rzędy bez zatrzymywania → niekontrolowany wzrost.
Non-butching/sortowanie nagrywanie → słaba kompresja i burza IOPS.
Wymieszaj OLTP i długie skany na tej samej puli dysków.
Brak polityki pozasądowej → duplikaty i wzdęcia.
Histogramy z setkami wiader → koszt × 10 bez korzyści.
15) Lista kontrolna wdrażania
- Określić wskaźniki, ich rodzaje i jednostki; naprawić kontrakt na nazwę/etykietę.
- Wybierz silnik (TSDB vs SQL/Column) i język zapytania (PromQL/SQL).
- Projekt retencji/rollup (gorący/ciepły/zimny) i ILM.
- Konfiguracja ingest: WAL/partie/sortowanie, okna poza zamówieniem.
- Włącz kompresję (delta-of-delta/XOR/RLE), optymalne kawałki.
- Kardynalność kontroli: kwoty, wpisy, polityka opt-out.
- Konfiguracja HA/federacji i zdalnego zapisu/odczytu.
- Deski rozdzielcze SLO i mierniki pamięci masowej (połknięcie/zapytanie/przechowywanie).
- Polityka izolacji bezpieczeństwa/najemcy i brak PII w etykietach.
- Regularny „dzień gry”: zasypka, utrata węzła, przepięcie.
16) FAQ
P: Co wybrać do monitorowania infrastruktury: Prometheus lub ClickHouse/Timescale?
Odp.: Do monitorowania pochodzenia i PromQL - Prometheus + przechowywanie długoterminowe (Victoria/M3). Dla scenariuszy BI/magazynu i SQL - Timescale/ClickHouse.
P: Jak przechowywać kwantyle bez ciężkich podsumowań?
Odp.: Użyj histogramu ze schludnymi wiadrami i oblicz kwantyle na żądanie; lub t-digest/CKMS w kruszywach.
P: Jak radzić sobie z nieporządkiem?
Odp.: Wprowadź okno tolerancji (5-15 min) i deterministyczną politykę dedup; dla telemetrii z mobilnej/krawędzi - okno jest szersze.
P: Kiedy potrzebny jest rollup?
Odp.: Zawsze> 30-90 dni: agregaty zmniejszają rozmiar × 10-100 i przyspieszają analitykę.
P: Czy możliwe jest mieszanie dzienników i mierników?
Odp.: Przechowywać osobno (formaty/zapytania są różne). Do korelacji należy stosować Przykładowy/TraceID i deski rozdzielcze, ale nie dodawać wszystkiego do jednej tabeli.
17) Kwoty całkowite
Efektywne przechowywanie serii czasowych to kontrakt metryczny + dyscyplina znaczników, właściwe spożycie (WAL/zagęszczenie), kompresja i polityka retencji/rollup. Dodaj kontrolę kardynalności, NA/federację i obserwowalność samego sklepu - a otrzymasz przewidywalny p95, rozsądny koszt od miesięcy do lat i opór gwałtowny.