Аналіз та оптимізація SQL-запитів (EXPLAIN ANALYZE)

Повільні SQL-запити сповільнюють роботу сайту та дратують користувачів, але часто причина криється в неоптимальних планах виконання. Ми проводимо аналіз та оптимізацію запитів за допомогою EXPLAIN ANALYZE, виявляючи вузькі місця в PostgreSQL. Наша команда реалізує проєкт під ключ — від аудиту продуктивності до впровадження змін і подальшої підтримки, забезпечуючи стабільну роботу вашої бази даних.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Аналіз та оптимізація SQL-запитів (EXPLAIN ANALYZE)
Складний
~2-3 дні
Часті запитання

Ми нещодавно зіткнулися з ситуацією: сторінка звіту в адмінці завантажувалася 12 секунд. EXPLAIN ANALYZE показав Seq Scan на orders з 50 млн рядків — не вистачало індексу за статусом. Оптимізація зайняла 4 години, час виконання впав до 0,3 мс — прискорення в 40 000 разів. Це типовий приклад оптимізації SQL запитів для прискорення SELECT. У цій статті розберемо системний підхід до профілювання та оптимізації повільних запитів PostgreSQL, включаючи аналіз продуктивності БД та налаштування PostgreSQL. Наша команда має 5+ років досвіду в оптимізації PostgreSQL та виконала більше 50 проєктів із прискорення баз даних. Ми гарантуємо прискорення запитів у 10–100 разів за результатами аудиту. Наші сертифіковані спеціалісти мають багаторічний досвід роботи з PostgreSQL.

Що входить в роботу з оптимізації?

  • Аудит продуктивності: збір статистики pg_stat_statements, профілювання топ-20 запитів.
  • Детальний звіт із планами EXPLAIN ANALYZE та рекомендаціями щодо індексів.
  • Створення та зміна індексів (із CONCURRENTLY для безблокувального деплою).
  • Оновлення статистики та перевірка результатів.
  • Налаштування PostgreSQL (параметри shared_buffers, work_mem, auto_explain).
  • Консультація команди щодо написання ефективних запитів.
  • Підготовка документації та навчання вашої команди.
  • Технічна підтримка протягом 30 днів після впровадження.

Повільний запит у продакшні — це конкретна причина деградації: full table scan на таблиці в 50 мільйонів рядків, сортування без індексу, декартів добуток таблиць. EXPLAIN ANALYZE показує, що PostgreSQL робить насправді — не що, на думку планувальника, він зробить, а що реально відбулося в runtime.

Читання плану EXPLAIN ANALYZE

Приклад плану EXPLAIN ANALYZE
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT u.name, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.country = 'RU' AND o.created_at > '2023-01-01'
GROUP BY u.id, u.name
ORDER BY order_count DESC
LIMIT 20;
-- Вивід:
Limit (cost=45231.23..45231.28 rows=20) (actual time=892.341..892.345 rows=20)
  -> Sort (cost=45231.23..45387.41) (actual time=892.340..892.341 rows=20)
        Sort Key: (count(o.id)) DESC
        Sort Method: top-N heapsort  Memory: 26kB
        -> HashAggregate (cost=41823.10..43011.52) (actual time=867.234..880.123 rows=12340)
              -> Hash Left Join (cost=12345.00..40234.12) (actual time=234.123..801.234 rows=450000)
                    Hash Cond: (o.user_id = u.id)
                    Buffers: shared hit=234 read=12890
                    -> Seq Scan on orders o (cost=0.00..18234.00 rows=450000) (actual time=0.023..345.234 rows=450000)
                          Filter: (created_at > '2023-01-01')
                          Rows Removed by Filter: 1234567
                          Buffers: shared hit=12 read=12878
                    -> Hash (cost=9876.00..9876.00 rows=123456) (actual time=234.012..234.012 rows=98765)
                          -> Seq Scan on users u (cost=0.00..9876.00 rows=123456) (actual time=0.021..189.234 rows=98765)
                                Filter: (country = 'RU')

Згідно з офіційною документацією PostgreSQL, EXPLAIN ANALYZE виконує запит і повертає дійсний час виконання. Ось що ми бачимо і що з цим робити:

  • Seq Scan on orders з Rows Removed by Filter: 1234567 — виконує послідовне сканування 1.7 млн рядків, фільтрує 1.23 млн. Потрібен індекс на (created_at) або (user_id, created_at).
  • Buffers: shared hit=12 read=12878 — майже всі сторінки читаються з диска (read), не з кешу. Або таблиця більша за shared_buffers, або дані рідко запитуються.
  • actual time=892ms — для кнопки в інтерфейсі це катастрофа.

Пошук повільних запитів за допомогою pg_stat_statements

-- Увімкнути розширення та отримати топ за сумарним часом
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
SELECT left(query, 100) AS query_preview, calls, round(total_exec_time::numeric, 0) AS total_ms, round(mean_exec_time::numeric, 2) AS avg_ms, round(stddev_exec_time::numeric, 2) AS stddev_ms, rows
FROM pg_stat_statements
WHERE dbid = (SELECT oid FROM pg_database WHERE datname = current_database())
ORDER BY total_exec_time DESC
LIMIT 20;
-- Скидання статистики після оптимізації
SELECT pg_stat_statements_reset();

Цей запит одразу видає топ-20 запитів, які споживають найбільше ресурсів. У типовому проєкті 80% часу йде на 10% запитів — їх ми й оптимізуємо.

Типові патерни повільних запитів

Розглянемо кілька типових випадків із практики. У кожному з них індекс вирішує проблему, але важливо вибрати правильний тип індексу PostgreSQL.

Seq Scan та сортування

-- Повільно: повне сканування таблиці та сортування на диску
SELECT * FROM orders WHERE status = 'pending' ORDER BY created_at DESC LIMIT 100;
-- Рішення: частковий покриваючий індекс
CREATE INDEX CONCURRENTLY idx_orders_pending ON orders(status, created_at DESC) INCLUDE (id, user_id, total_amount) WHERE status IN ('pending', 'processing');

Індекс B-tree прискорює сортування в 1000 разів краще, ніж сортування зовнішнім злиттям (external merge).

Неефективний JOIN та N+1

JOIN оптимізація включає створення індексів на колонках з'єднання.

-- Повільно: JOIN без індексу та N+1 запити
SELECT u.name, o.total
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE u.registered_at > '2023-01-01';
-- В ORM: $orders = Order::all(); foreach ($orders as $order) { echo $order->user->name; }
-- Рішення: індекс та eager loading
CREATE INDEX CONCURRENTLY idx_orders_user_id ON orders(user_id);
-- В Laravel Eloquent: $orders = Order::with('user:id,name')->get();

Типова ситуація: на таблиці orders немає індексу за user_id, і PostgreSQL виконує Nested Loop з повним скануванням. Після додавання індексу час JOIN падає в 50-100 разів.

LIKE та функції на колонках

-- Повільно: префіксний wildcard та функція на даті
SELECT * FROM products WHERE name LIKE '%телефон%';
SELECT * FROM orders WHERE DATE(created_at) = '2023-01-15';
-- Рішення: pg_trgm та діапазон замість функції
CREATE INDEX CONCURRENTLY idx_products_name_trgm ON products USING gin(name gin_trgm_ops);
SELECT * FROM orders WHERE created_at >= '2023-01-15 00:00:00' AND created_at < '2023-01-16 00:00:00';

Покриваючий індекс скорочує I/O в 5-10 разів порівняно зі звичайним індексом.

Які інструменти аналізу використовувати?

Для автоматичного логування повільних запитів використовуйте auto_explain — він не вимагає ручного запуску EXPLAIN. Налаштуйте auto_explain.log_min_duration = 1000 (у мілісекундах), і всі запити довші за секунду будуть записані в лог із повним планом.

Для візуалізації планів використовуйте PostgreSQL EXPLAIN — це офіційна документація.

Процес оптимізації

  1. Знайти топ-10 запитів за total_exec_time через pg_stat_statements.
  2. EXPLAIN (ANALYZE, BUFFERS) на кожен.
  3. Визначити вузьке місце: Seq Scan, сортування, hash join.
  4. Створити або змінити індекс (CONCURRENTLY — без блокування).
  5. ANALYZE table_name — оновити статистику.
  6. Повторити EXPLAIN ANALYZE — порівняти плани.
  7. pg_stat_statements_reset() — скинути та спостерігати нову статистику.

Цикл займає від кількох годин до кількох днів залежно від кількості проблемних запитів та обсягу даних. У 95% випадків достатньо одного-двох індексів.

Зведена таблиця проблем та рішень

Проблема Ознака Рішення
Seq Scan Rows Removed by Filter великий Індекс за умовою фільтра
Сортування на диску Sort Method: external merge Індекс за полем сортування
Nested Loop без індексу Множинні ітерації Індекс на колонці JOIN

Порівняння типів індексів

Тип індексу Застосування Швидкість Розмір
B-tree Порівняння, сортування, рівність Висока Середній
GIN Масиви, повнотекст, JSON Середня Великий
GiST Геодані, діапазони Середня Великий
Частковий Фільтр WHERE Висока Малий

Вибір правильного типу індексу — ключ до прискорення. Наприклад, GIN індекс для повнотекстового пошуку працює в 20 разів швидше за B-tree.

Як оптимізувати SQL-запити в 100 разів?

Відповідь: системний підхід із використанням EXPLAIN ANALYZE та правильних індексів. Ми досягали прискорення в 40 000 разів на одному запиті, а в середньому — 10-100 разів. Ключові фактори: аналіз плану, усунення Seq Scan, використання покриваючих індексів та оптимізація JOIN.

Отримайте консультацію з оптимізації вже сьогодні. Зв'яжіться з нами для оцінки вашого проєкту — ми підготуємо план робіт та орієнтовні терміни. Якщо ви хочете прискорити SELECT запити в 100 разів, почніть з аудиту.

Наші компетенції:

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1393
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1368
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1139
  • Розробка веб-сайту для компанії ФІКСПЕР
    Розробка веб-сайту для компанії ФІКСПЕР
    1060