1. Селекторы и логика фильтрации временных рядов
Селекторы и логика фильтрации временных рядов
Ввод имени метрики в строку поиска интерфейса Prometheus в production-среде крупной компании — гарантированный способ «повесить» вкладку браузера. Если система собирает данные с тысяч контейнеров, простой запрос http_requests_total вернет десятки или сотни тысяч уникальных временных рядов. TSDB попытается извлечь их все, API передаст огромный JSON, а браузер не справится с отрисовкой такого количества графиков. Работа с метриками начинается не с агрегации, а с безжалостного отсечения лишних данных.
Инструмент для этой задачи — селектор (Selector). Это базовый синтаксический конструкт PromQL, который определяет, какие именно временные ряды из базы данных должны попасть в итоговую выборку для дальнейших вычислений.
Анатомия базового селектора и скрытая метка
Синтаксически селектор состоит из имени метрики и набора условий фильтрации, заключенных в фигурные скобки. Запрос http_requests_total{method="POST"} читается как: найти все временные ряды с именем http_requests_total, у которых значение метки method строго равно POST.
Внутренняя механика Prometheus обрабатывает этот запрос несколько иначе, чем кажется на первый взгляд. В архитектуре TSDB имя метрики не является какой-то особой сущностью. Это точно такая же метка, как и все остальные, просто она скрыта под системным ключом __name__.
Когда пользователь отправляет запрос http_requests_total{method="POST"}, парсер PromQL автоматически переписывает его в следующий вид:
{__name__="http_requests_total", method="POST"}
Понимание этого механизма открывает доступ к нестандартным выборкам. Например, можно использовать операторы регулярных выражений не только для обычных меток, но и для самого имени метрики, если обратиться к __name__ напрямую.
Операторы фильтрации: от точного совпадения к исключениям
PromQL поддерживает четыре оператора сопоставления (match operators) внутри селектора. Они позволяют строить как жесткие, так и гибкие условия поиска.
Оператор = (Точное совпадение)
Оставляет только те ряды, где значение метки посимвольно совпадает с указанной строкой. Запрос node_filesystem_avail_bytes{fstype="ext4"} отбросит файловые системы xfs, tmpfs и любые другие.
Оператор != (Точное несовпадение)
Работает как инверсия. Запрос http_requests_total{status!="200"} вернет все запросы, которые завершились с любым статусом, кроме 200. Это классический паттерн для поиска аномалий: вместо того чтобы перечислять все возможные коды ошибок (400, 401, 403, 500, 502), мы просто исключаем успешный статус.
Оператор =~ (Совпадение по регулярному выражению)
Позволяет использовать всю мощь стандарта RE2 (движок регулярных выражений от Google, используемый в Go и Prometheus). Запрос http_requests_total{status=~"5.."} найдет все серверные ошибки (500, 501, 502 и так далее). Точка в регулярном выражении означает «любой символ».
Оператор !~ (Несовпадение по регулярному выражению)
Исключает ряды, значения меток которых подпадают под паттерн. Если система работает в нескольких дата-центрах (например, dc="us-east-1", dc="eu-west-1", dc="ap-south-1"), запрос process_cpu_seconds_total{dc!~".-west-."} отфильтрует все метрики из западных регионов.
Скрытые механизмы регулярных выражений в PromQL
Использование операторов =~ и !~ таит в себе ловушку, на которой регулярно спотыкаются инженеры, привыкшие к стандартным регулярным выражениям в Python, bash или JavaScript.
В классических движках регулярное выражение ищет подстроку. Если написать паттерн 20, он успешно сработает для строк 200, 420, 2024.
В PromQL регулярные выражения всегда неявно привязаны к началу и концу строки. Парсер автоматически оборачивает любой переданный паттерн в символы начала ^ и конца $ строки.
!Интерактивный симулятор привязки регулярных выражений
Если написать запрос http_requests_total{status=~"50"}, PromQL будет искать строку, которая строго равна 50. Он не найдет статус 500 или 502. Чтобы найти все статусы, начинающиеся на 5, необходимо явно указать, что после пятерки могут идти любые символы в любом количестве, используя конструкцию . (точка и звездочка). Правильный запрос выглядит так: http_requests_total{status=~"5."}.
Эта особенность кардинально меняет подход к написанию фильтров.
Паттерн «Логическое ИЛИ»
Самое частое применение регулярных выражений в PromQL — это реализация логического «ИЛИ» для значений одной метки. Допустим, нужно выбрать метрики только для production и staging сред, проигнорировав dev и test.
Вместо несуществующего синтаксиса env="prod" OR env="staging", используется регулярное выражение с символом вертикальной черты |:
{env=~"prod|staging"}
Благодаря неявной привязке к началу и концу строки, этот паттерн сработает строго для значений prod и staging. Если бы привязки не было, он мог бы случайно захватить значение preprod (так как оно содержит prod).
Паттерн «Содержит подстроку»
Если нужно найти все сервисы, в названии которых есть слово payment (например, payment-gateway, stripe-payment-processor, legacy-payments), паттерн должен учитывать любые символы как до, так и после искомого слова:
{job=~".payment."}
Множественная фильтрация и пересечение множеств
Селектор может содержать любое количество условий, разделенных запятой. Запятая в PromQL выполняет роль строгого логического «И» (AND).
Запрос http_requests_total{job="api", status=~"5.*", method!="GET"} означает, что временной ряд попадет в итоговую выборку только в том случае, если он удовлетворяет всем трем условиям одновременно.
!Пересечение множеств при фильтрации по меткам
С точки зрения производительности TSDB, порядок перечисления меток внутри фигурных скобок не имеет значения. Prometheus использует инвертированный индекс (inverted index). При выполнении запроса движок находит список ID временных рядов для каждого условия по отдельности, а затем выполняет операцию пересечения этих списков.
Если условие job="api" соответствует 10 000 рядам, а условие status="500" соответствует 50 рядам, Prometheus быстро найдет их пересечение, независимо от того, какое условие написано первым. Оптимизатор запросов сам решает, с какого индекса начать фильтрацию, обычно начиная с того, который дает наименьшее количество совпадений (наименьшую кардинальность).
Поиск по отсутствующим меткам и пустым значениям
Одна из самых неочевидных концепций модели данных Prometheus заключается в том, что система не делает различий между отсутствующей меткой и меткой с пустым значением (пустой строкой "").
Если приложение экспортирует метрику database_queries_total, и для успешных запросов метка error_code вообще не добавляется в код, а для ошибочных добавляется (например, error_code="timeout"), то с точки зрения TSDB у успешных запросов метка error_code существует, просто ее значение равно "".
Это архитектурное решение экономит место на диске и в оперативной памяти: не нужно хранить метаданные о том, какие ключи существуют, а какие нет. Если ключа нет — его значение считается пустой строкой.
Из этого вытекают два мощных паттерна фильтрации:
1. Поиск рядов, у которых метка отсутствует (или пуста)
Чтобы найти все успешные запросы к базе данных из примера выше, используется точное совпадение с пустой строкой:
database_queries_total{error_code=""}
Этот запрос вернет все временные ряды database_queries_total, в которых разработчик не указал метку error_code.
2. Поиск рядов, у которых метка присутствует (имеет любое значение)
Чтобы найти только проблемные запросы, где код ошибки был явно задан, используется оператор несовпадения с пустой строкой:
database_queries_total{error_code!=""}
Альтернативный вариант через регулярные выражения:
database_queries_total{error_code=~".+"} (точка и плюс означают «один или более любых символов»).
Использование !="" предпочтительнее с точки зрения читаемости и производительности, так как точное несовпадение вычисляется быстрее, чем проход через движок регулярных выражений RE2.
Глобальные запросы: фильтрация без имени метрики
Поскольку имя метрики — это просто скрытый лейбл __name__, PromQL технически разрешает писать запросы, в которых имя метрики не указано вообще. Селектор может начинаться сразу с фигурных скобок.
Запрос {job="payment-gateway"} является абсолютно валидным. Он просит TSDB: «верни мне вообще все временные ряды, принадлежащие этому сервису, независимо от того, как называется сама метрика». В ответ прилетят и счетчики HTTP-запросов, и загрузка процессора, и использование памяти, и бизнес-метрики — всё в одной куче.
В повседневной работе такие запросы используются редко, так как складывать или сравнивать между собой метрики разных типов (например, байты памяти и количество запросов) не имеет математического смысла. Однако этот механизм незаменим в задачах SRE и траблшутинга инфраструктуры.
Сценарий 1: Аудит кардинальности
Если инстанс Prometheus начал потреблять слишком много оперативной памяти, инженер может использовать глобальный запрос для подсчета количества временных рядов, генерируемых конкретным сервисом, чтобы выявить виновника взрыва кардинальности. (Для самого подсчета потребуется функция count(), которая будет разобрана в следующих главах).
Сценарий 2: Поиск метрик по паттерну имени
Иногда точное имя метрики неизвестно, но известна ее часть. Используя явное обращение к __name__, можно применить регулярное выражение:
{__name__=~"process_cpu.*", job="node-exporter"}
Этот запрос вернет все метрики, названия которых начинаются на process_cpu, собранные с серверов.
При выполнении глобальных запросов существует жесткое ограничение безопасности: запрос должен содержать хотя бы одно условие, которое не совпадает с пустой строкой. Запрос {} или запрос {job=~".*"} не пройдет валидацию. Prometheus отклонит его, так как выполнение такого запроса потребовало бы поднять в оперативную память абсолютно всю базу данных, что привело бы к немедленному падению сервера по OOM (Out of Memory).
Освоение селекторов — это фундамент работы с PromQL. Умение быстро отсекать 99% ненужных данных с помощью точных совпадений, регулярных выражений и манипуляций с пустыми метками определяет, насколько эффективно инженер сможет строить дальнейшие математические агрегации. Независимо от сложности итоговой формулы, ее вычисление всегда начинается с того, какие именно временные ряды были захвачены селектором на первом шаге.