Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → Хранение временных рядов: сжатие и партиционирование

Хранение временных рядов: как устроены метрики под капотом

Хранение временных рядов: модель данных, дельта-дельта и XOR-сжатие, downsampling, retention и партиционирование по времени.

Прямой ответ

Временной ряд — последовательность пар «метка времени — значение», относящихся к одному набору меток (имя метрики плюс теги). Хранилища временных рядов оптимизированы под три свойства таких данных: запись только в конец, неизменяемость старых точек и чтение целыми диапазонами.

Модель данных:

  • Серия идентифицируется именем метрики и полным набором пар «тег — значение». Добавление нового тега создаёт новую серию.
  • Кардинальность — число уникальных серий. Это главный ограничитель: каждая серия требует собственной структуры в памяти и на диске.
  • Точка — пара (timestamp, value), обычно 8 байт на каждую часть до сжатия.

Сжатие — то, что делает такие объёмы приемлемыми. Классический подход описан в статье о базе Gorilla:

  1. Метки времени: дельта-дельта. Точки поступают с примерно равным интервалом, поэтому хранят не саму метку, а разницу с предыдущей, а затем разницу разниц. Для стабильного интервала значение часто равно нулю и кодируется одним битом.
  2. Значения: XOR. Последовательные значения плавающей точки часто близки или равны; хранят результат XOR с предыдущим значением. При совпадении — один бит; при близости — только значащие биты.
  3. Блочное хранение. Точки серии пишутся блоками, что улучшает сжатие и ускоряет чтение диапазонов.

Результат: вместо 16 байт на точку типично получается 1–2 байта, то есть сжатие в 8–15 раз.

Второй ключевой механизм — downsampling (прореживание) и retention (срок хранения):

Слой Разрешение Срок
Горячий Исходное (10–15 с) 7–30 дней
Тёплый 1–5 минут 3–12 месяцев
Холодный 1 час Годы

Логика простая: для расследования инцидента нужны секунды за последние дни, а для тренда за год достаточно часовых агрегатов.

#VictoriaMetrics: прореживание задаётся флагом хранения
# -retentionPeriod=12m  и  -downsampling.period=30d:5m,180d:1h
-- Вариант на PostgreSQL: партиционирование по времени + агрегирующая таблица
CREATE TABLE metrics (
    ts timestamptz NOT NULL, series_id bigint NOT NULL, value double precision
) PARTITION BY RANGE (ts);

CREATE TABLE metrics_1h AS
SELECT date_trunc('hour', ts) AS ts, series_id,
       avg(value) AS avg_value, max(value) AS max_value
FROM metrics GROUP BY 1, 2;

Практика: проектирование и эксплуатация

1. Контролируйте кардинальность. Высокая кардинальность — основная причина деградации систем мониторинга. Метки, содержащие идентификатор запроса, e-mail пользователя или случайный идентификатор, порождают миллионы серий, каждая из которых стоит памяти. Практика: держите в метках только то, по чему планируете фильтровать и агрегировать; детали — в логи.

2. Разделяйте метрики и события. Метрика отвечает на вопрос «сколько в момент времени», лог — «что произошло». Попытка выразить события через метрики ведёт к росту кардинальности, а попытка считать процентили по логам — к дорогой аналитике. О распределении и процентилях — в статье про гистограммы метрик.

3. Планируйте слои хранения заранее. Перенос данных из горячего слоя в холодный после накопления объёма сложнее, чем настройка в начале. Решение о том, какие агрегаты нужны, принимается до того, как исходные данные удалены по сроку хранения.

4. Партиционируйте по времени. Это даёт дешёвое удаление устаревших данных: удаление партиции — операция с метаданными, а не массовое удаление строк. Подробнее — в статье про партиционирование PostgreSQL.

5. Учитывайте стоимость запросов с большим диапазоном. Запрос за год по исходному разрешению читает огромное число точек. Хорошая система предлагает автоматическое прореживание на стороне сервера (step в Prometheus API) — см. VictoriaMetrics.

6. Дедупликация при нескольких источниках. Если одни и те же метрики приходят с двух реплик, хранилище должно уметь отбрасывать дубли; иначе объём и стоимость запросов удваиваются — см. долгосрочное хранение в Mimir.

Рекомендации:

  1. Измеряйте объём до запуска. Оценка: число серий × частота снятия × срок хранения × байт на точку после сжатия. Ошибка в кардинальности на порядок даёт ошибку в объёме на порядок.
  2. Не храните метки с высокой энтропией. Каждое новое значение тега — новая серия навсегда, пока не истечёт срок хранения.
  3. Проверяйте, что агрегаты считают то же самое. Среднее от средних не равно среднему; для процентилей нужны гистограммы, а не усреднённые значения.
  4. Разделяйте hot и cold физически. Горячий слой — быстрый диск и память, холодный — объектное хранилище. Смешивание приводит к тому, что архивные запросы мешают оперативным.
  5. Тестируйте удаление по сроку. Данные, которые «должны удалиться через год», часто остаются из-за неверной настройки; проверка дешевле, чем выяснение при переполнении диска.

Практический сценарий: хранилище метрик исчерпало память за неделю

Контекст: кластер мониторинга, рассчитанный на полгода работы, начал перезапускаться из-за нехватки памяти через неделю после запуска.

  1. Диагностика. Число активных серий оказалось 4,1 млн вместо ожидавшихся 300 тысяч.
  2. Причина. В одну метрику добавили тег с идентификатором обработанного задания. Каждое задание создавало новую серию, которая оставалась в памяти до конца срока хранения.
  3. Дополнительно. Второй источник роста — тег с путём запроса, включавший параметры строки запроса: вместо нескольких сотен значений получилось более 200 тысяч.
  4. Действия. (а) Тег с идентификатором задания убрали, идентификаторы перенесли в журналы; (б) путь запроса заменили на шаблон маршрута без параметров; (в) добавили лимит на число серий на метрику и алерт при превышении порога; (г) настроили прореживание: исходное разрешение 15 дней, далее пятиминутные агрегаты.
  5. Результат. Число серий снизилось до 260 тысяч, потребление памяти — с 58 до 9 ГБ, срок хранения увеличили до года без роста затрат.

Вывод: объём хранилища временных рядов определяется не количеством серверов и не частотой снятия, а кардинальностью меток. Один неудачный тег стоит дороже, чем ошибка в выборе системы хранения.

FAQ

Подойдёт ли обычная реляционная база для метрик? Для небольших объёмов — да, особенно с партиционированием по времени. На больших объёмах специализированные хранилища выигрывают за счёт сжатия, ориентированного именно на пары «время — значение», и за счёт эффективной работы с высокой кардинальностью.

Что такое downsampling и чем он отличается от агрегации? Прореживание уменьшает разрешение старых данных, сохраняя их характер: обычно оставляют среднее, минимум, максимум и счётчик за интервал. Это форма агрегации, применяемая автоматически по сроку хранения, а не по запросу.

Почему нельзя восстановить процентиль из средних значений? Процентиль нелинеен: средний из почасовых p95 не равен p95 за сутки. Для корректного пересчёта нужны гистограммы или исходные точки.

Как выбрать срок хранения? От задач: расследование инцидентов — дни и недели с исходным разрешением, сравнение с прошлым периодом — месяцы с прореживанием, отчётность и ёмкостное планирование — годы с часовым шагом. Начинать стоит с короткого горячего слоя и длинного холодного.

Источники: VictoriaMetrics docs, Prometheus docs (storage), VLDB (Gorilla: дельта-дельта и XOR-сжатие) — ссылки в шапке.