PostgreSQL: индексы, EXPLAIN и медленные запросы
Медленные запросы — это боль каждого разработчика, работающего с реляционными базами данных. В этой статье мы разберём, почему PostgreSQL может тормозить, и как превратить запрос за 8 секунд в запрос за 40 мс с помощью правильных индексов и анализа плана выполнения.
Не доверяйте только EXPLAIN
Команда EXPLAIN ANALYZE показывает не только теоретический план, но и реальное время выполнения каждой операции. Это ключевой инструмент для профилирования производительности.
EXPLAIN ANALYZE SELECT * FROM users WHERE created_at < '2023-01-01' AND status = 'active';
Обратите внимание на строки Execution Time и Planning Time. Если Execution Time огромное, значит, движок действительно много работает. Если Planning Time огромное, проблема в статистике или планировщике. Всегда запускайте именно ANALYZE, чтобы получить реальные цифры.
B-tree, GIN, BRIN — когда что использовать
Индексы — это ускорители. Но не все индексы подходят для всех задач. Выбор неправильного типа может даже замедлить работу базы данных.
B-tree
Стандарт для равенства и диапазонов. Работает для операторов =, >, <. Идеален для даты и ID.
GIN
Для неупорядоченных наборов данных: массивы, JSONB, полнотекстовый поиск. Отлично подходит для поиска по вложенным объектам.
BRIN
Блок-ридж индексы. Очень маленькие, но работают только если данные монотонны (например, временные метки или последовательные ID).
GiST
Обобщённый поиск. Используется для геометрии, полнотекстового поиска и операторов, не поддерживаемых GIN.
pg_stat_statements
Чтобы найти "самого виновного" запроса, нужно включить расширение pg_stat_statements в конфигурации.
- Шаг 1:
CREATE EXTENSION pg_stat_statements; - Шаг 2:
SELECT query, calls, total_exec_time, rows FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;
Это покажет, какие запросы выполняются чаще всего и сколько времени на них уходит в сумме. Часто оказывается, что один запрос с большим rows тормозит всё приложение, хотя выполняется редко.
PgBouncer vs Встроенный пул
Поддержка множества подключений может съесть CPU и RAM сервера. В PostgreSQL есть встроенный механизм (пулер), но профессионалы предпочитают PgBouncer.
PgBouncer держит в памяти пул активных соединений и пересоздаёт их при необходимости. Это снижает нагрузку на БД в десятки раз. Встроенный пул в PostgreSQL (параметр max_connections) имеет свои ограничения по производительности при высокой нагрузке.
Когда он мешает
Автовакуум — это необходимость. Но если он работает слишком агрессивно, он может блокировать запись.
Посмотрите на системные логи. Если видите ошибки lock contention или autovacuum: unable to obtain lock, значит, нагрузка высокая, а таблица большая. Настройте параметры autovacuum_vacuum_scale_factor и autovacuum_vacuum_cost_delay, чтобы распределить нагрузку на вакуумирование равномернее.
8 секунд → 40 мс
Клиент столкнулся с зависанием API. Запрос на выборку логов с фильтрами по дате и статусу работал 8 секунд.
Решение: Добавили составной индекс CREATE INDEX idx_logs_user_time ON logs(user_id, event_time DESC);.
Разница в 200 раз. Составной индекс позволил отсеять ненужные строки ещё на этапе чтения индекса.
Хотите, чтобы ваша база данных работала быстрее?
GlyphKit предоставляет выделенные серверы с NVMe-дисками и предустановленными PostgreSQL для быстрого старта.