Кардинальность лейблов

Урок про главную ловушку Prometheus — взрыв кардинальности лейблов.

Кардинальность метрики — это число уникальных временных рядов, которое она порождает; оно равно произведению количеств уникальных значений всех её лейблов.

Это причина номер один, по которой Prometheus съедает память и тормозит. Один неосторожный лейбл способен превратить одну метрику в миллионы рядов. Разберём, как этого избежать.

Как считается кардинальность

Каждая уникальная комбинация значений лейблов — отдельный ряд. Перемножьте количества значений каждого лейбла, и получите число рядов.

http_requests_total{method, status, instance}
  method:   5 значений
  status:   8 значений
  instance: 10 значений
  итого:    5 * 8 * 10 = 400 рядов   (нормально)

А теперь добавим лейбл user_id с миллионом значений:

  + user_id: 1 000 000 значений
  итого:     400 * 1 000 000 = 400 000 000 рядов   (катастрофа)

Что нельзя класть в лейблы

  • ID пользователей, сессий, заказов.
  • Email, IP-адреса.
  • URL с встроенными параметрами (/user/123/order/456).
  • Timestamp и любые непрерывно растущие значения.

Такие данные — для логов и трейсов, не для лейблов метрик.

Как считать кардинальность вручную

labels = {
    "method": 5,
    "status": 8,
    "instance": 10,
    "endpoint": 20,
}

series = 1
for name, count in labels.items():
    series *= count

print("Лейблы и их мощности:", labels)
print("Всего временных рядов:", series)

Вывод:

Лейблы и их мощности: {'method': 5, 'status': 8, 'instance': 10, 'endpoint': 20}
Всего временных рядов: 8000

Как работает под капотом

Prometheus держит индекс всех серий в памяти. Каждый ряд — это запись в индексе плюс буфер свежих сэмплов. Поэтому стоимость определяется не объёмом значений во времени, а именно числом уникальных рядов. Миллионы рядов — это гигабайты RAM и медленные запросы, даже если каждый ряд хранит всего пару точек. Нормализуйте лейблы заранее: вместо сырого URL кладите шаблон маршрута (/user/:id/order/:id).

Частые ошибки

  • Сырой URL в лейбле. Каждый уникальный путь — новый ряд; кладите шаблон маршрута.
  • Динамические лейблы из пользовательского ввода. Атакующий может намеренно раздуть кардинальность.
  • Не следить за ростом рядов. Отслеживайте prometheus_tsdb_head_series и алертьте на резкий рост.

Итог

  • Кардинальность = произведение мощностей всех лейблов.
  • ID, email, URL с параметрами в лейблах метрик запрещены.
  • Нормализуйте значения лейблов и следите за числом рядов.
Проверьте себя
1. Как оценить кардинальность метрики?
AСложить число значений всех лейблов
BПеремножить количества уникальных значений всех лейблов
CВзять максимум среди лейблов
DОна всегда равна числу инстансов
2. Что НЕ стоит класть в лейбл метрики?
AHTTP-метод
BКод ответа
CID пользователя
DИмя job
3. Почему высокая кардинальность так бьёт по Prometheus?
AЗамедляет сеть
BИндекс всех рядов держится в памяти — миллионы рядов съедают RAM и тормозят запросы
CЛомает формат /metrics
DУдаляет старые данные