GH GambleHub

Хранение временных рядов

1) Зачем отдельная архитектура для временных рядов

Временные ряды (time series) — это последовательности пар (timestamp, value) с тегами (labels), которые характеризуются:
  • Высокой скоростью записей (ingest) и периодичностью.
  • Чтениями по диапазонам времени (scan + агрегаты/оконные функции).
  • Взрывной кардинальностью из-за комбинаций тегов.
  • Необходимостью ретеншна (ограничения по сроку хранения) и downsampling (сжатие по времени).
  • Отсюда — специальная модель хранения, форматы компрессии и протоколы запросов.

2) Модель данных и контракт метрик

2.1 Именование и теги

metric_name: глагол/существительное в единственном числе (`http_requests_total`, `cpu_usage_seconds_total`).
labels: ключи-атрибуты (`job`, `instance`, `dc`, `pod`, `status`, `method`).
Инварианты: не менять семантику имени, добавлять версии (`metric_v2`) при несовместимых изменениях.

2.2 Типы рядов

Gauge (снимок), Counter (нарастающий итог), Histogram/Summary (распределения/квантили), Event/Span (трейс-точки).
Для финансов/плотностей — фиксируйте единицы измерения и агрегируемость (суммируется/усредняется).

2.3 Политика ретеншна и роллапов

Горячая детализация (секунды/1–10 мин) → теплые агрегаты (5m/1h) → холодные (1d/1w).
Для counter — хранить rate/deriv агрегаты.

3) Путь записи: прием, буферизация, компакт

3.1 Ingest-пайплайн

Scrape (pull, Prometheus) или push (OTLP/StatsD/Graphite), часто через gateway/agent.
Буферизация в WAL (write-ahead log), затем компакция в сегменты/блоки (LSM-подобная архитектура).
Batching и сортировка по времени повышают сжатие и скорость.

3.2 Обработка out-of-order и дублей

Окно допусков (lateness window, например 5–15 мин) + политика: `drop | upsert | keep-last`.
Дедупликация по `(series_id, timestamp)` с версионностью или «последняя запись побеждает».

3.3 Компрессия

Delta-of-delta для меток времени, Gorilla/XOR для float, RLE и varint для целых, dictionary для тегов.
Оптимальный размер блока («чанка») 1–8К точек — компромисс между IOPS и CPU.

4) Схемы хранения: TSDB vs SQL/колоночники

4.1 Специализированные TSDB

Prometheus (локально, короткий ретеншн, PromQL, remote_write).
VictoriaMetrics/M3/InfluxDB — горизонтальное масштабирование, долгий ретеншн, remote read.
Форматы блоков оптимизированы под range scan + тендинговые агрегации.

4.2 Реляционные/колоночные движки

TimescaleDB (PostgreSQL): гипертаблицы, чанки по времени/пространству, continuous aggregates.
ClickHouse: MergeTree/TTL/материализованные представления, отличная компрессия и агрегации по времени.
Выбор — по экосистеме запросов (SQL vs PromQL), требованиям к join/BI и операционным навыкам команды.

5) Схема и примеры

5.1 TimescaleDB: гипертаблица + 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: агрегирующее хранение

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) Кардинальность: как не «взорвать» хранилище

6.1 Правила

Ограничивайте label cardinality (число уникальных значений). Не включайте `user_id`, `request_id`, `trace_id`.
Нормализуйте «многозначные» теги (категории → коды).
Используйте LowCardinality типы (в CH), словари/деревья меток (в TSDB).

6.2 Контроль и алерты

Метрики: `series_count`, `label_values{label}`, top-N «дорогих» рядов.
Политики отказа записи при превышении лимита кардинальности per tenant/job.

6.3 Истории/гистограммы

Для high-cardinality лучше хранить агрегаты (histogram buckets) и pre-rollup; квантили вычислять онлайн на агрегатах.

7) Ретеншн, downsampling и tiered-storage

7.1 Политики

Hot: 3–30 дней секундной/минутной детализации.
Warm: 90–365 дней 5m/1h агрегатов.
Cold: годы дневных агрегатов, архив в объектном хранилище (S3/Glacier) с Parquet.

7.2 Техники

Continuous aggregates (Timescale), материализованные представления (CH), retention + rollup tasks (Victoria/M3/Influx).
Tiered storage: «горячие блоки» локально, «холодные» в объектном с локальным кэшем.

8) Запросы и языки

8.1 PromQL (пример)

promql rate(http_requests_total{job="api",status=~"5.."}[5m])

Ищем темп ошибок 5xx по API.

8.2 SQL-агрегаты по окнам

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 Аномалии (эскиз)

Z-score/ESD по оконной статистике, STL-декомпозиция сезонности; хранить результаты в отдельном ряду `anomaly=1/0`.

9) Интеграции и протоколы

OTLP (OpenTelemetry): метрики/трейсы/логи, экспортеры на агентах (otel-collector) → TSDB/кликхаус/объектное.
StatsD/Graphite: простые счетчики/таймеры; прокси на edge, далее — конверсия в единый формат.
Kafka/NATS: буфер для всплесков ingest, replayer для бэкфилла; консьюмеры пишут батчами.

Пример Kafka → ClickHouse (псевдо):
text kafka(topic=metrics) -> stream processor (normalize/tags) -> CH INSERT INTO metrics_cpu FORMAT RowBinary

10) Доступность, HA и федерация

Replica/HA-пары TSDB или федерация Prometheus (уровень регион → глобаль).
Remote read/write для долговременного хранения и централизованных дашбордов.
Shard-by-label/time: равномерное распределение ingest, locality по `dc/tenant`.

11) Наблюдаемость самого хранилища

11.1 Метрики

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`.
Кардинальность: `series_count`, top-labels.

11.2 SLO

«p99 latency для диапазона 1h ≤ 200 мс при QPS≤500».
«Ingest-drop ≤ 0.01% при burst до X samples/sec».
«Compaction backlog < 10 min».

11.3 Алерты

Рост `series_count` > Y%/час.
Очередь компакции/flush > порога.
Доля out-of-order > N%, dedup/late-drops.

12) Безопасность и мульти-тенантность

Изоляция по `tenant` (лейбл в ключах, отдельные таблицы/базы, квоты).
Санитизация меток (запрет PII), контроль размеров/значений.
Шифрование «в покое» и на транспорте, аудит доступа к «чувствительным» метрикам.

13) Эксплуатационные практики

Прогрев и холодный старт: pin «горячих» блоков в кэше, prefetch последних N часов.
Backfill: отдельные пайплайны с низким приоритетом, не смешивать с онлайном.
Версионирование схемы: миграции с параллельной записью (dual-write) и последующим свитчем.
Бюджет хранения: контроль `cost_per_TB_month` + forecast роста кардинальности.

14) Анти-паттерны

Теги с высокой кардинальностью (user_id, uuid) → взрыв рядов.
«Вечные» ряды без ретеншна → бесконтрольный рост.
Запись без батчинга/сортировки → плохая компрессия и IOPS-шторм.
Смешивание OLTP и длинных сканов на одном пуле дисков.
Отсутствие политики out-of-order → дубликаты и раздувание.
Гистограммы с сотнями бакетов → стоимость ×10 без пользы.

15) Чек-лист внедрения

  • Определите метрики, их типы и единицы; зафиксируйте контракт имен/лейблов.
  • Выберите движок (TSDB vs SQL/колоночник) и язык запросов (PromQL/SQL).
  • Спроектируйте ретеншн/роллап (hot/warm/cold) и ILM-таски.
  • Настройте ingest: WAL/батчи/сортировку, окна out-of-order.
  • Включите компрессию (delta-of-delta/XOR/RLE), оптимальные чанки.
  • Контролируйте кардинальность: квоты, алерты, политики отказа.
  • Настройте HA/федерацию и remote-write/read.
  • Дашборды SLO и метрики хранилища (ingest/query/storage).
  • Политики безопасности/тенант-изоляции и отсутствие PII в лейблах.
  • Регулярные «game day»: бэкфилл, потеря узла, всплеск ingest.

16) FAQ

Q: Что выбрать для мониторинга инфраструктуры: Prometheus или ClickHouse/Timescale?
A: Для нативного мониторинга и PromQL — Prometheus + длительный storage (Victoria/M3). Для BI/складских сценариев и SQL — Timescale/ClickHouse.

Q: Как хранить квантили без тяжелых summary?
A: Используйте histogram с аккуратными бакетами и вычисляйте квантили при запросе; или t-digest/CKMS в агрегатах.

Q: Как поступать с out-of-order?
A: Введите окно допуска (5–15 мин) и детерминированную политику dedup; для телеметрии из мобильных/edge — окно шире.

Q: Когда нужен rollup?
A: Всегда при ретеншне > 30–90 дней: агрегаты снижают размер ×10–100 и ускоряют аналитику.

Q: Можно ли смешивать логи и метрики?
A: Храните раздельно (форматы/запросы разные). Для корреляции используйте Exemplar/TraceID и дэшборды, но не складывайте все в одну таблицу.

17) Итоги

Эффективное хранилище временных рядов — это контракт метрик + дисциплина тегов, грамотный ingest (WAL/компакция), компрессия и политики ретеншна/роллапов. Добавьте контроль кардинальности, HA/федерацию и наблюдаемость самого стора — и вы получите предсказуемые p95, разумную стоимость на месяцы-годы и устойчивость к всплескам.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Telegram
@Gamble_GC
Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

Нажимая кнопку, вы соглашаетесь на обработку данных.