Найдите дорогостоящий запрос до изменения базы данных
Начинайте от действия пользователя: загрузка категории, поиск статьи, добавление в корзину или отправка заказа. Зафиксируйте маршрут, число запросов, время в базе данных, возвращённые строки и время запроса. Медленная страница также может зависеть от кода приложения или другой зависимости, поэтому не диагностируйте только по времени загрузки страницы.
План запроса — это маршрут базы данных по таблицам, индексам, join‑ам, сортировкам и агрегатам. В PostgreSQL EXPLAIN показывает этот маршрут. EXPLAIN ANALYZE выполняет оператор и отчитывает фактические строки и тайминги, поэтому используйте безопасный репрезентативный запрос. Большой разрыв между оценкой и фактическим числом строк — повод проверить статистику или условия, а не автоматическая причина для добавления индекса.
Привяжите кандидатный запрос к маршруту и результату, чтобы отчёт только по дашборду не превзошёл важность медленной оплаты заказа.
Запишите тайминги до изменений и сопоставимый трафик без логирования идентификаторов клиентов, токенов или данных карт.
Проектируйте индексы под реальный путь доступа
Индекс — это поддерживаемая структура, которая помогает находить строки или возвращать их в порядке; это не универсальный переключатель скорости. Начните с фильтров важного запроса, ключей соединения и порядка сортировки, затем подтвердите предложенный индекс в плане. PostgreSQL документирует, что подходящий B-tree индекс может удовлетворить ORDER BY без отдельной сортировки, особенно при небольшом LIMIT.
Индексы добавляют объём хранения и работу при записи, потому что вставки, обновления и удаления поддерживают их. Для запроса, который требует большую долю таблицы, последовательное сканирование и явная сортировка могут быть быстрее. Тестируйте на данных, похожих на производственные, и оставляйте индекс только когда выгода оправдывает его стоимость.
Используйте фактические фильтры запроса и ORDER BY, включая детерминированный tie-breaker, когда интерфейсу нужен стабильный порядок.
Проверьте, используется ли индекс и какую нагрузку он даёт на записи; пересматривайте решение по мере изменения данных и трафика.
Сделайте пагинацию стабильной и недорогой
Пагинация по номерам страниц обычно использует LIMIT плюс OFFSET. Всегда задавайте предсказуемый порядок. PostgreSQL предупреждает, что без ORDER BY LIMIT может вернуть непредсказуемый набор; разные значения LIMIT или OFFSET могут приводить к неоднозначным подмножествам, потому что оптимизатор может выбирать разные планы.
Большие смещения требуют внимания, потому что пропущенные строки всё равно нужно вычислить. Для глубоких каталогов, истории заказов или архивного просмотра рассмотрите курсорную (keyset) пагинацию: запрашивайте строки после последнего увиденного стабильного значения сортировки и уникального tie-breaker. Определите, как новые или удалённые записи будут появляться, прежде чем менять интерфейс.
Возвращайте наименьшую полезную страницу вместо неограниченного результата.
Решите, действительно ли интерфейсу нужен точный общий счёт на каждом запросе.
Кэшируйте чтение, не выдавая неверный ответ
Кэширование переиспользует ранее полученный результат вместо восстановления его из базы данных. Начинайте с HTTP‑слоя для публичных повторяемых страниц и версионированных ресурсов, потому что попадание в кэш может избежать работы приложения и базы данных. web.dev рекомендует долгоживущий кэш для версионированных URL; изменение URL — практический механизм инвалидирования.
Политика кэша — это решение корректности. Общий кэш может переиспользовать один ответ для разных пользователей, поэтому не делитесь выводом, специфичным для аккаунта, корзины, цены или авторизации, если только он не разработан и не протестирован для такого охвата. Cache-Control private ограничивает повторное использование браузерным кэшем, а no-store указывает кэшам не сохранять ответ.
Разделяйте публичный каталог или статьи и вывод, зависящий от сессии, местоположения и прав.
Определите, кто может видеть устаревшее значение, как долго и какой путь инвалидирования или повторной валидации используется.
Защитите ёмкость соединений вместо простого увеличения предела
Соединение — это живая клиентская сессия к базе данных. Рабочие процессы, cron‑задачи, предпросмотры или повторы могут исчерпать слоты даже когда отдельные запросы выглядят приемлемо. max_connections в PostgreSQL контролирует параллельные соединения, задаётся при старте сервера, и при повышении выделяет больше ресурсов, включая общий пул памяти. Увышение без модели нагрузки может сдвинуть точку отказа.
Назначьте один бюджет на веб‑воркеры, фоновые задания, администрирование и реплики. Используйте ограниченные пулы там, где это уместно, сохраняйте запас для восстановления и применяйте таймауты, чтобы покинутые задачи не ожидали бесконечно. Безопасные числа зависят от движка, памяти, микса запросов и ограничений провайдера; подтвердите их с оператором.
Включите веб‑процессы, очереди, планировщики, миграции, инструменты отчётности и задачи развертывания.
Капируйте параллелизм и используйте backoff, чтобы ошибка не умножала спрос на соединения.
Наблюдайте стоимость запросов с учётом контекста и приватности
Ранжируйте работу базы данных по совокупному воздействию, а не по одному медленному образцу. Умеренно медленный запрос, вызываемый тысячи раз, может важнее редкого отчёта. pg_stat_statements в PostgreSQL агрегирует планирование и выполнение запросов, включая вызовы, общее и среднее время выполнения, строки и буферную активность. Для его добавления и удаления нужна конфигурация сервера и рестарт; отслеживание планирования может добавить накладные расходы.
Привязывайте группы запросов к маршруту, версии релиза и временным окнам. OpenTelemetry рекомендует собирать текст запросов по‑умолчанию только после санации, заменяя литералы, и использовать сводку с низкой кардинальностью для группировки. Это помогает владельцам видеть тренды без экспорта данных клиентов или создания метки для каждого значения.
Проверяйте длительность, суммарное время, вызовы, ошибки, соединения и строки; каждый показатель выявляет разные режимы отказа.
Используйте параметризованные запросы и удостоверяйтесь, что литералы удаляются до выхода трасс или логов из приложения.
Выпускайте изменения в базе как обратимую работу по надёжности
Относитесь к индексу, переписыванию запроса, политике кэша, настройке пула или изменению схемы как к производственному изменению с владельцем, метрикой успеха и решением об откате. Валидируйте в репрезентативной среде, выпускайте ограниченному срезу и наблюдайте затронутый путь. Быть быстрее — не значит успешно, если изменение возвращает неверные, отсутствующие, дублирующие или устаревшие данные.
Схемные изменения требуют осторожности, потому что версии приложения могут перекрываться. Руководство Google Cloud описывает последовательность совместимости: выпустите приложение, которое работает со старой и новой схемой, измените схему, затем выпустите функцию, которая её использует. Это сохраняет путь отката приложения. Блокировки и поведение онлайн‑изменений различаются по движку и управляемому сервису; для высоконагруженных или связанных с платежами систем запросите квалифицированное ревью.
Назовите маршрут, сигналы производительности и корректности и порог, который остановит раскатку.
Проверьте производительность и корректность в контролируемом срезе перед расширением; отрабатывайте путь возврата вне инцидента.
