Наблюдаемость бэкенда: запросы, SQL, очереди и зависимости
Бэкенд может возвращать 200 OK и при этом оставаться нездоровым. Запросы могут постепенно замедляться, количество обращений к базе данных — расти, задачи — накапливаться в очереди, а внешний API — занимать большую часть времени ответа. Одних графиков CPU и памяти обычно недостаточно, чтобы понять причину таких проблем.
Наблюдаемость бэкенда — это сбор достаточного количества сигналов, чтобы понимать, что делает система, где тратится время, что именно ломается и почему. Для большинства прикладных бэкендов удобной отправной точкой являются четыре области: запросы, обращения к базе данных, очереди и зависимости.
Краткий ответ
Наблюдаемость бэкенда должна позволять проследить выполнение работы через всю систему:
request → application code → database → queue → worker → downstream dependency
Метрики показывают общие закономерности, например рост задержки или количества ошибок. Трейсы связывают операции между компонентами. Логи дают подробные события и контекст. OpenTelemetry позволяет связывать эти сигналы с помощью идентификаторов trace и span, а W3C Trace Context определяет стандартный механизм передачи tracing-контекста между сервисами. (opentelemetry.io)
Цель не в том, чтобы собирать всё подряд. Нужно собрать достаточно данных, чтобы инженер мог перейти от «API работает медленно» к «этот endpoint ждёт конкретный запрос к базе, очередь или внешний сервис».
Что нужно наблюдать?
| Область | Полезные сигналы | На какие вопросы отвечает |
|---|---|---|
| Запросы | частота, распределение задержки, статус, ошибки | Какие endpoint'ы работают медленно или завершаются с ошибкой? |
| Запросы к БД | длительность, шаблон запроса, количество вызовов, ошибки | База данных вызывает задержку? |
| Очереди | глубина, время ожидания, время обработки, ошибки | Асинхронная обработка не успевает за нагрузкой? |
| Зависимости | задержка, ошибки, таймауты | Причина проблемы находится в другом сервисе? |
Эта модель дополняет более общее наблюдение за состоянием сервисов. В материалах Google SRE latency, traffic, errors и saturation описываются как четыре фундаментальных сигнала мониторинга пользовательских систем. (sre.google)
Начните с запросов
Запросы обычно являются лучшей отправной точкой, потому что отражают работу, которую пользователи или другие системы фактически попросили бэкенд выполнить.
Для HTTP API полезно отслеживать:
- количество запросов в секунду;
- HTTP-статус или результат операции приложения;
- длительность запроса;
- распределение задержки, например p50, p95 или p99;
- route или operation;
- сервис и версию развертывания.
Не стоит ориентироваться только на среднее время ответа. Среднее значение может выглядеть приемлемо, даже если небольшая часть запросов выполняется крайне медленно. Google SRE отдельно обращает внимание на tail latency, а измерения на основе гистограмм позволяют увидеть такое распределение. Prometheus также документирует гистограммы как механизм представления распределений, включая длительность запросов. (sre.google)
OpenTelemetry определяет стандартные semantic conventions для HTTP client и server spans. Единообразные атрибуты и названия операций упрощают сравнение трейсов между разными фреймворками и сервисами. (opentelemetry.io)
INTERNAL LINK: Мониторинг производительности HTTP-запросов
Отслеживайте запросы к базе
Предположим, /api/products начал работать медленно.
Сам HTTP-запрос показывает, что проблема существует, но не обязательно объясняет её причину. Трейс может выглядеть так:
GET /api/products 680 ms
├── authentication 12 ms
├── SELECT products 610 ms
└── response serialization 18 ms
Теперь направление расследования меняется. Вместо оптимизации контроллера нужно проверить операцию с базой данных.
OpenTelemetry определяет database client spans для представления операций с базой данных, наблюдаемых вызывающим приложением. (opentelemetry.io)
Полезные сигналы базы данных включают длительность запросов, частоту вызовов, ошибки, нагрузку на connection pool и нормализованный идентификатор запроса. Записывать каждый SQL-запрос целиком не всегда уместно: текст запроса может содержать чувствительные данные или значения с высокой кардинальностью, поэтому instrumentation должна учитывать правила очистки и обработки данных конкретной базы данных и observability-стека.
В PostgreSQL pg_stat_statements может агрегировать статистику планирования и выполнения SQL-запросов, что помогает находить дорогие или слишком часто выполняющиеся шаблоны запросов. (postgresql.org)
После выявления подозрительного запроса EXPLAIN показывает план его выполнения. EXPLAIN ANALYZE действительно выполняет запрос и показывает фактическое количество строк и время выполнения отдельных узлов плана, что помогает проверить, связана ли проблема с оценками, сканированием, join-операциями или другими этапами выполнения. (postgresql.org)
INTERNAL LINK: Диагностика медленных SQL-запросов
Отдельно измеряйте время в очереди
Очереди меняют само понимание задержки.
Рассмотрим задачу обработки изображения:
created
↓
waiting in queue: 14.2 s
↓
worker starts
↓
processing: 820 ms
↓
completed
Если смотреть только на время выполнения worker'а, операция заняла бы 820 мс. Но с точки зрения приложения задача выполнялась более 15 секунд.
Это важное различие.
Как минимум стоит отслеживать объём ожидающей работы, время ожидания, длительность обработки, а также ошибки и повторные попытки, если используемая очередь предоставляет такие сигналы. Одна только глубина очереди может вводить в заблуждение: десять лёгких и десять тяжёлых задач не обязательно создают одинаковую нагрузку.
Google SRE описывает чрезмерную длину очереди одновременно как проблему задержки и ресурсов: ожидающие запросы потребляют ресурсы и могут усиливать перегрузку, если работа поступает быстрее, чем система успевает её обрабатывать. (sre.google)
Distributed tracing также усложняется в асинхронных системах, поскольку producer и consumer выполняются в разное время. Messaging conventions OpenTelemetry отдельно рассматривают передачу контекста между producer и consumer, чтобы операции можно было связать даже через messaging-инфраструктуру. (opentelemetry.io)
INTERNAL LINK: Мониторинг очередей и производительности worker'ов
Измеряйте зависимости
Большинство production-бэкендов не работают изолированно. Один запрос может зависеть от:
Application
├── PostgreSQL
├── Redis
├── payment API
├── object storage
└── internal user service
Поэтому каждая важная зависимость должна быть наблюдаемой как отдельная операция, а не исчезать внутри общего времени выполнения запроса.
Для каждой важной зависимости обычно полезно отслеживать задержку, объём запросов, частоту ошибок, поведение таймаутов и вызывающую операцию. Это позволяет отличить две совершенно разные ситуации:
API slow
└── application code slow
от:
API slow
└── payment provider: 2.4 s
От этого зависит, в каком направлении продолжать расследование.
Связывайте весь trace
Distributed observability становится значительно полезнее, когда одну и ту же операцию можно проследить через границы сервисов.
W3C Trace Context определяет для этого HTTP-заголовки traceparent и tracestate. traceparent переносит стандартизированную информацию о trace, благодаря чему взаимодействующие системы могут сохранять связь между операциями даже при использовании разных tracing-реализаций. (w3.org)
Концептуально это выглядит так:
Browser
│
▼
API Gateway
trace-id: abc
│
▼
Order Service
trace-id: abc
│
├── PostgreSQL
│ trace-id: abc
│
└── Payment Service
trace-id: abc
OpenTelemetry называет этот механизм context propagation. Downstream-сервис получает tracing-контекст и создаёт собственный span как часть того же distributed trace. (opentelemetry.io)
Без propagation инженеры видят изолированные операции. С propagation они видят причинно связанную цепочку.
Практический процесс диагностики
Представим, что пользователи сообщают: checkout иногда занимает пять секунд.
Начните с метрик запроса. Проверьте, действительно ли выросла задержка checkout и затрагивает ли проблема все запросы или в основном медленный tail.
Затем откройте медленный trace:
POST /checkout 5.1 s
├── validate cart 8 ms
├── SELECT inventory 31 ms
├── payment request 4.8 s
└── create order 76 ms
База данных, вероятно, не является первым местом для расследования. Большую часть trace занимает зависимость от платёжного сервиса.
Проверьте метрики и логи этой зависимости за тот же период. Ищите рост удалённой задержки, таймауты, повторные попытки или ошибки соединения. После внесения изменений снова измерьте те же сигналы запроса и зависимости.
Процесс выглядит так:
симптом → измерение → подозреваемый компонент → подтверждение → исправление → повторное измерение
Это значительно надёжнее, чем оптимизировать компонент только потому, что он первым показался подозрительным.
Типичные ошибки observability
Первая ошибка — сбор инфраструктурных метрик без контекста приложения. Загрузка CPU может показать saturation, но не объяснит, какой endpoint или запрос к базе её вызвал.
Вторая — логировать всё, но ничего не связывать между собой. Миллион записей в логах трудно исследовать, если их невозможно связать с конкретным запросом или задачей. OpenTelemetry поддерживает добавление идентификаторов trace и span в log records именно для такой корреляции. (opentelemetry.io)
Третья ошибка — игнорировать время ожидания в асинхронных системах. Время обработки worker'ом и полная end-to-end задержка задачи — это разные показатели.
Четвёртая — использовать среднее значение как единственное представление задержки. Tail latency может выявить проблемы, которые скрываются за приемлемым средним значением.
Наконец, избегайте неконтролируемой кардинальности telemetry-данных. User ID, полные URL с произвольными параметрами, необработанные SQL-значения, request body или уникальные job ID могут сделать систему метрик дорогой и потенциально раскрыть чувствительные данные. Контекст с высокой кардинальностью лучше хранить в системах, рассчитанных на такие данные, а не превращать каждое значение в metric label.
Рекомендация SeoNest
Начинайте с границы входящего запроса и постепенно инструментируйте систему дальше.
Для каждой критически важной backend-операции сначала обеспечьте наблюдаемость задержки запросов, traffic и ошибок. Затем представьте обращения к базе данных, messaging-операции и важные downstream-зависимости как отслеживаемые операции. Сохраняйте trace context между HTTP-вызовами и асинхронными сообщениями и связывайте логи с тем же контекстом выполнения.
Не начинайте с создания десятков dashboard'ов. Сначала ответьте на более простой вопрос:
Может ли инженер взять один медленный или неудачный запрос и определить, откуда пришла задержка или ошибка?
Если нет, в observability бэкенда всё ещё отсутствуют важные данные.
FAQ
Достаточно ли одних логов?
Обычно нет. Логи дают подробные события, метрики лучше подходят для обнаружения агрегированных закономерностей, а трейсы показывают, как операции связаны внутри запроса. Корреляция всех трёх типов данных полезнее, чем их использование по отдельности.
Нужно ли превращать каждый SQL-запрос в trace span?
Важные операции с базой данных должны быть наблюдаемыми, однако необходимо учитывать объём instrumentation, чувствительные данные в запросах и стоимость telemetry. Автоматическую instrumentation следует настраивать осознанно, а не считать безопасной при неограниченном объёме данных.
Что нужно отслеживать в очередях?
Хорошей отправной точкой являются размер очереди или backlog, время ожидания, длительность обработки, ошибки, повторные попытки и доступная мощность worker'ов. Точный набор сигналов зависит от messaging-системы и характера нагрузки.
Чем мониторинг отличается от observability?
Мониторинг обычно отслеживает заранее определённые сигналы и состояния системы. Observability использует telemetry — метрики, трейсы и логи — для исследования поведения системы, включая сбои, которые не были заранее предусмотрены при создании dashboard'ов или alert'ов.
Требует ли distributed tracing использования OpenTelemetry?
Нет. W3C Trace Context является vendor-neutral стандартом, и разные tracing-реализации могут поддерживать distributed context propagation. OpenTelemetry предоставляет широко используемые API, SDK, semantic conventions и инструменты для создания и передачи telemetry. (w3.org)
Итог
Хорошая observability бэкенда превращает абстрактную проблему задержки в последовательность измеримых операций.
Не останавливайтесь на утверждении «запрос выполнялся две секунды».
Нужно определить, были ли эти две секунды потрачены на выполнение application code, ожидание SQL, нахождение задачи в очереди или вызов другого сервиса. Запросы показывают симптом; обращения к базе, очереди и зависимости показывают, куда ушло время; трейсы и связанные с ними логи объединяют доказательства.
Именно это отличает ситуацию, когда вы просто знаете, что бэкенд нездоров, от ситуации, когда можете объяснить почему.
Sources
- W3C — Trace Context. W3C Recommendation. Определяет стандартизированную передачу
traceparentиtracestate. W3C Trace Context - OpenTelemetry — Semantic Conventions for HTTP Spans. Официальная спецификация OpenTelemetry. OpenTelemetry HTTP Spans
- OpenTelemetry — Semantic Conventions for Database Client Spans. Официальная спецификация OpenTelemetry. OpenTelemetry Database Spans
- OpenTelemetry — Semantic Conventions for Messaging Spans. Официальная спецификация OpenTelemetry. OpenTelemetry Messaging Spans
- OpenTelemetry — Context Propagation and Logging Specification. Официальная документация по distributed context и корреляции логов. OpenTelemetry Context Propagation OpenTelemetry Logging
- Google — Site Reliability Engineering: Monitoring Distributed Systems. Rob Ewaschuk, edited by Betsy Beyer. Рассматривает latency, traffic, errors, saturation и tail latency. Google SRE — Monitoring Distributed Systems
- Google — Site Reliability Engineering: Addressing Cascading Failures. Рассматривает очереди, перегрузку, задержку и saturation. Google SRE — Addressing Cascading Failures
- Prometheus — Histograms and Summaries. Официальная документация о представлении распределений, включая задержку запросов. Prometheus Histograms and Summaries
- PostgreSQL —
pg_stat_statementsand UsingEXPLAIN. Официальная документация PostgreSQL по статистике запросов и анализу планов выполнения. PostgreSQL pg_stat_statements PostgreSQL Using EXPLAIN


