Что особенного во временных рядах
Временной ряд — поток измерений с метками времени: метрики серверов, IoT-датчики, цены, события. У такой нагрузки специфический профиль: запись почти всегда append (новые точки в конце), данные приходят в порядке времени и в огромном объёме, обновления и удаления точек редки.
Запросы тоже специфичны: агрегаты по временным окнам («среднее за 5 минут за сутки»), а свежие данные нужны точно, старые — обычно в сжатом виде.
Модель данных
Типичная модель (InfluxDB): measurement (что измеряем, напр. cpu), tags (индексируемые метки-измерения: host, region), fields (сами значения) и timestamp. Tags задают, как ряд разрезается и фильтруется, поэтому их кардинальность критична.
Высокая кардинальность тегов (например, уникальный id запроса в теге) взрывает число рядов и убивает производительность — классическая ошибка проектирования time-series.
Хранение и сжатие
Точки хранят чанками по времени (time-based partitioning), что делает запросы по диапазону и удаление старых данных дешёвыми — удаляется целый чанк, а не миллионы строк.
Time-series сильно сжимаются специальными алгоритмами: delta/delta-of-delta для возрастающих timestamp и Gorilla-кодирование для значений. Похожие данные рядом + специализированное сжатие дают на порядок меньший объём, чем в обычной БД.
Downsampling и retention
Хранить посекундные данные годами бессмысленно. Retention policy автоматически удаляет данные старше срока, а continuous queries / downsampling заранее считают агрегаты пониженного разрешения (секунды → минуты → часы).
Так свежие данные детальны, а исторические — компактны, что и нужно для дашбордов и трендов.
TimescaleDB и когда хватит обычной БД
Не всегда нужна отдельная система: TimescaleDB — расширение PostgreSQL, которое добавляет hypertable (автопартиционирование по времени), сжатие и continuous aggregates, сохраняя полноценный SQL и экосистему Postgres.
Если объём метрик умеренный и хочется не плодить технологии — обычная реляционная БД с партиционированием по времени и правильными индексами вполне справится. Специализированный TSDB оправдан на больших потоках и при потребности в сильном сжатии.
Практический ориентир для выбора: считайте не только объём, но и профиль. Если данные пишутся потоком, читаются почти всегда агрегатами по окнам, а старые точки не нужны в исходном разрешении — специализированное хранилище окупится сжатием и retention. Если же метрики нужно соединять со справочниками и бизнес-сущностями, ценность полноценного SQL обычно перевешивает.
-- TimescaleDB: обычная таблица превращается в hypertable
create table metrics (
ts timestamptz not null,
host text not null,
cpu double precision
);
select create_hypertable('metrics', 'ts'); -- автопартиционирование по времени
-- агрегат по временным окнам: среднее за 5 минут
select time_bucket('5 minutes', ts) as bucket,
host,
avg(cpu)
from metrics
where ts > now() - interval '1 day'
group by bucket, host
order by bucket desc;
-- retention: удаляется целый чанк, а не миллионы строк
select add_retention_policy('metrics', interval '90 days');
Как отвечать на собеседовании
Сильный ответ: опишите профиль (append-heavy, time-ordered, агрегаты по окнам), назовите риск высокой кардинальности тегов, объясните time-chunking + сжатие и роль retention/downsampling, и упомяните TimescaleDB как мост от обычного SQL.
Начинайте с профиля нагрузки, а не с названий продуктов: запись почти всегда append в конец, данные приходят упорядоченными по времени и в большом объёме, обновления и удаления отдельных точек редки, а читают почти всегда агрегатами по окнам. Именно этот профиль оправдывает отдельный класс хранилищ.
Кардинальность тегов — любимый вопрос. Формулируйте так: теги задают, как ряд разрезается, и каждая уникальная комбинация тегов порождает отдельный ряд. Положить в тег request_id или user_id значит взорвать число рядов и убить производительность. Это самая частая ошибка проектирования, и знание её сразу выделяет кандидата.
Про хранение свяжите два механизма с их выгодой: чанки по времени делают дешёвыми и запросы по диапазону, и удаление старого (дропаем чанк целиком вместо DELETE на миллионы строк), а специализированное сжатие (delta-of-delta для timestamp, Gorilla для значений) даёт на порядок меньший объём.
Retention и downsampling объясняйте одной мыслью: хранить посекундные данные годами бессмысленно, поэтому свежее держим детальным, а старое заранее агрегируем до минут и часов.
Завершите зрелой оговоркой: отдельный TSDB нужен не всегда. При умеренном объёме обычный PostgreSQL с партиционированием по времени справится, а TimescaleDB даёт середину — hypertable, сжатие и continuous aggregates без ухода из SQL и экосистемы Postgres.