Back

Как выявить запросы к базе данных, которые замедляют ваше приложение

Как выявить запросы к базе данных, которые замедляют ваше приложение

Ваш дашборд загружается за 200 мс локально. В продакшене это занимает 8 секунд. Пользователи жалуются. Ваш PM спрашивает, в чём проблема. Вы подозреваете базу данных, но не можете это доказать.

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

Ключевые выводы

  • Начинайте диагностику с симптомов, видимых пользователю, а не с внутренностей базы данных — подтвердите, куда реально уходит время, прежде чем исследовать запросы.
  • Используйте EXPLAIN ANALYZE для чтения планов запросов и выявления последовательных сканирований, вложенных циклов и несоответствий в количестве строк.
  • Отслеживайте латентность p95 и p99 вместо средних значений, чтобы обнаружить выбросы, которые вредят реальным пользователям.
  • Различайте проблемы, связанные с CPU, I/O и конкуренцией за блокировки — каждая требует разных подходов к оптимизации.

Начните с симптомов, видимых пользователю

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

Обратите внимание на эти паттерны:

  • Постоянная медлительность: каждый запрос к /api/orders занимает 3+ секунды. Вероятно, один дорогой запрос.
  • Каскадные запросы: страница делает 47 последовательных вызовов API. Классическая проблема N+1 запросов.
  • Непостоянная латентность: один и тот же эндпоинт варьируется от 50 мс до 5 секунд. Может быть конкуренция за блокировки или промахи кеша.
  • Всплески таймаутов: запросы иногда полностью падают. Проверьте блокировки таблиц или исчерпание пула соединений.

Browser DevTools и логи вашего API дают вам первую подсказку. Если сетевое время минимально, но время ответа высокое, узкое место — в бэкенде. Если логи бэкенда показывают, что большая часть времени уходит на вызовы к базе данных, вы нашли цель.

Основные техники обнаружения для анализа производительности запросов

Читайте план запроса

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

PostgreSQL:

EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;

MySQL (8.0.18+):

EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;

SQLite:

EXPLAIN QUERY PLAN SELECT * FROM orders WHERE user_id = 123;

Ищите последовательные сканирования больших таблиц, вложенные циклы с большим количеством строк и несоответствия между оценочным и фактическим количеством строк. План, показывающий “Seq Scan” на таблице с миллионом строк, когда вы ожидали поиск по индексу, сразу выявляет проблему.

Примечание: планировщики запросов меняются между версиями баз данных. План, который работал в PostgreSQL 14, может отличаться в PostgreSQL 16. Не предполагайте, что обновления автоматически исправляют плохие запросы.

Включите логирование медленных запросов

Настройте вашу базу данных на логирование запросов, превышающих пороговое значение.

PostgreSQL использует pg_stat_statements для агрегированной статистики выполнения. Для логирования отдельных запросов используйте log_min_duration_statement. Performance Schema и sys schema в MySQL выявляют медленные запросы без устаревшего query cache. SQLite требует измерения времени на уровне приложения, так как не имеет встроенного логирования медленных запросов.

Устанавливайте пороги консервативно в продакшене. Начните со 100 мс, затем ужесточайте по мере исправления очевидных проблем.

Измеряйте перцентили, а не средние значения

Среднее время запроса скрывает выбросы. Запрос со средним временем 50 мс может достигать 2 секунд на p99 — стабильно влияя на 1% ваших пользователей.

Отслеживайте латентность p95 и p99. Если ваш p99 в 10 раз больше медианы, у вас есть проблема с хвостовой латентностью, которую стоит исследовать.

Различайте CPU, I/O и конкуренцию за блокировки

Высокое время выполнения запроса означает разные вещи:

  • Нагрузка на CPU: сложные вычисления, большие сортировки или операции с регулярными выражениями. План запроса показывает высокую “стоимость” на узлах, требовательных к вычислениям.
  • Нагрузка на I/O: чтение большого объёма данных с диска. Следите за последовательными сканированиями и отсутствующими индексами.
  • Конкуренция за блокировки: запросы ожидают других транзакций. Проверьте pg_stat_activity (PostgreSQL) или SHOW PROCESSLIST (MySQL) на заблокированные запросы.

Каждая требует разных подходов к оптимизации SQL-запросов. Добавление индекса не поможет агрегации, нагружающей CPU.

Обнаружение с приоритетом на наблюдаемость через распределённую трассировку

OpenTelemetry позволяет коррелировать медленные запросы с конкретными спанами базы данных. Трассировка, показывающая ваш эндпоинт /api/dashboard, точно раскрывает, какие запросы потребили время.

Настройте вашу ORM или драйвер базы данных на отправку спанов с таймингом запросов. Вы чётко увидите проблему N+1: 50 идентичных SELECT спанов там, где должен быть один.

Предупреждение о безопасности: не логируйте полный SQL с параметрами в продакшен-трассировках. ID пользователей, email и другие персональные данные попадут в вашу платформу наблюдаемости. Вместо этого логируйте отпечатки запросов (параметризованные шаблоны).

Распространённые ловушки, которых следует избегать

  • Слепое добавление индексов: индексы ускоряют чтение, но замедляют запись. Проверьте, что план запроса действительно использует ваш новый индекс.
  • Опора на средние значения: это среднее 50 мс скрывает 5-секундные выбросы, вредящие реальным пользователям.
  • Универсальная настройка: “Увеличьте work_mem” — не универсальный совет. Сначала измерьте.
  • Логирование сырого SQL: риск для безопасности и соответствия требованиям. Используйте параметризованные отпечатки.

Заключение

Выявление узких мест производительности базы данных начинается с симптомов, а не с догадок. Отслеживайте медленные страницы до медленных эндпоинтов, медленные эндпоинты до медленных запросов, а медленные запросы до их планов выполнения. Измеряйте перцентили, различайте проблемы CPU/IO/блокировок и используйте распределённую трассировку для связи фронтенд-симптомов с бэкенд-причинами.

Только после этого следует оптимизировать.

Часто задаваемые вопросы

Проверьте логи вашего API на разбивку времени ответа. Если сетевая латентность низкая, но время ответа бэкенда высокое, узкое место — в сервере. Внутри вашего бэкенда измерьте время, потраченное на вызовы к базе данных, в сравнении с логикой приложения. Большинство фреймворков и APM-инструментов могут показать эту разбивку, помогая подтвердить, действительно ли запросы — настоящая причина.

EXPLAIN показывает план запроса, который база данных намерена использовать, без выполнения запроса. EXPLAIN ANALYZE фактически выполняет запрос и сообщает реальное время и количество строк. Используйте EXPLAIN для быстрых проверок и EXPLAIN ANALYZE, когда вам нужны точные данные о производительности. Будьте осторожны с EXPLAIN ANALYZE на операциях записи, так как он выполнит изменения.

Индексы потребляют хранилище и замедляют операции INSERT, UPDATE и DELETE, потому что база данных должна их поддерживать. Кроме того, планировщик запросов может не использовать ваш индекс, если оценит, что последовательное сканирование будет быстрее. Всегда проверяйте с помощью EXPLAIN ANALYZE, что ваш новый индекс действительно улучшает конкретные запросы, на которые вы нацелены.

Включите логирование запросов или используйте распределённую трассировку, чтобы увидеть все запросы, выполненные за один запрос. Ищите паттерны, где один и тот же шаблон запроса выполняется много раз с разными параметрами. ORM часто вызывают это при загрузке связанных записей по одной. Исправление обычно заключается в eager loading или объединении запросов в один с использованием IN-условия.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay