Оптимізація повільних SQL-запитів PostgreSQL

Оптимізація повільних SQL-запитів PostgreSQL

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

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

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

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Оптимізація повільних SQL-запитів PostgreSQL
Складний
~2-3 дні

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

Часті запитання

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

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1419
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    983
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1245
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    983
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    998

Оптимізація повільних SQL-запитів PostgreSQL

Ви чекаєте 20 секунд на завантаження звіту. Користувачі йдуть, база даних — вузьке горло. Типова ситуація: один запит до PostgreSQL виконується 5 секунд, а таких запитів сотні на хвилину. Ми розбираємося, чому це відбувається, і усуваємо проблему: знижуємо час відповіді БД у 3–5 разів на навантаженні. Діагностика триває день, оптимізація — ще пару днів. Всі зміни документуються, виміри до та після обов'язкові.

Повільні запити — основна причина поганого UX. 95% проблем продуктивності БД вирішуються одним із чотирьох методів: додаванням індексу, переписуванням запиту, денормалізацією або кешуванням. Досвід показує, що грамотна оптимізація 10–15 найважчих запитів може вивільнити до 40% ресурсів сервера. Розглянемо, як діагностувати та виправляти повільні запити в PostgreSQL.

Діагностика повільних запитів

За даними документації PostgreSQL, pg_stat_statements є стандартним інструментом для моніторингу. pg_stat_statements — перше розширення, яке потрібно ввімкнути на продакшні. Воно збирає статистику по кожному запиту: сумарний та середній час, кількість викликів, стандартне відхилення. Коефіцієнт варіації (coeff_var) допомагає виявити запити з нестабільним планом.

Процес діагностики за п'ять кроків:

  1. Увімкніть розширення pg_stat_statements (якщо вимкнено) та зберіть статистику за кілька годин.
  2. Виконайте запит топ-20 за total_exec_time.
  3. Для кожного підозрілого запиту отримайте план через EXPLAIN (ANALYZE, BUFFERS).
  4. Визначте вузли плану: Seq Scan, Nested Loop, Hash Join з Batches > 1.
  5. Застосуйте відповідну оптимізацію: додайте індекс, перепишіть запит, налаштуйте work_mem.
-- Ввімкнення pg_stat_statements shared_preload_libraries = 'pg_stat_statements' pg_stat_statements.max = 10000 pg_stat_statements.track = all -- Топ-20 за сумарним часом SELECT round(total_exec_time::numeric, 2) AS total_ms, round(mean_exec_time::numeric, 2) AS mean_ms, calls, round((stddev_exec_time / mean_exec_time * 100)::numeric, 1) AS coeff_var_pct, left(query, 120) AS query FROM pg_stat_statements WHERE calls > 100 ORDER BY total_exec_time DESC LIMIT 20; 

coeff_var_pct — коефіцієнт варіації: високий відсоток свідчить про нестабільний план (різні параметри дають кардинально різний час). Далі кожен підозрілий запит проганяємо через EXPLAIN ANALYZE:

EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) SELECT p.*, c.name AS category_name FROM products p JOIN categories c ON c.id = p.category_id WHERE p.status = 'published' AND p.created_at > NOW() - INTERVAL '30 days' ORDER BY p.created_at DESC LIMIT 50; 

Ключові вузли в плані:

  • Seq Scan на великій таблиці — немає індексу або планувальник вирішив, що індекс не вигідний.
  • Nested Loop з великою кількістю ітерацій — N+1 на рівні SQL.
  • Hash Join з Batches > 1 — не вистачає work_mem.
  • Sort без Index Scan на ORDER BY колонці — немає відповідного індексу.

Чому індекси не використовуються?

Навіть за наявності індексу планувальник може його проігнорувати. Основні причини:

  • Функція на колонці в WHERE (наприклад, DATE(created_at))
  • Низька селективність (індекс на булевій колонці зі зміщеним розподілом)
  • Сортування, що не збігається з порядком індексу

Рішення: переписуємо запит, щоб прибрати обгортки функцій, і створюємо складові індекси під конкретні патерни. Порядок колонок у складовому індексі: спочатку умови рівності, потім діапазонні та сортування.

Які антипатерни зустрічаються найчастіше?

-- Погано: SELECT * тягне зайві колонки; OFFSET збільшує навантаження; OR не використовує індекс; функція на колонці; NOT IN з NULL SELECT * FROM products WHERE category_id = 5 LIMIT 50 OFFSET 10000; SELECT * FROM users WHERE email = $1 OR phone = $1; SELECT * FROM orders WHERE DATE(created_at) = $1; SELECT * FROM products WHERE id NOT IN (SELECT product_id FROM order_items); -- Добре: лише потрібні поля; keyset pagination; UNION ALL; range condition; NOT EXISTS SELECT id, title, slug FROM products WHERE (created_at, id) > ($1, $2) ORDER BY created_at DESC LIMIT 50; SELECT * FROM users WHERE email = $1 UNION ALL SELECT * FROM users WHERE phone = $1 LIMIT 1; SELECT * FROM orders WHERE created_at >= $1 AND created_at < $2; SELECT p.* FROM products p WHERE NOT EXISTS (SELECT 1 FROM order_items oi WHERE oi.product_id = p.id); 

Порівняймо методи пагінації:

Порівняльна таблиця методів пагінації
Метод пагінації Навантаження на БД Підтримка випадкового доступу Вимагає індекс
OFFSET Зростає з номером сторінки Так Не обов'язковий
Keyset Константа Ні Обов'язковий
Кільцева навігація Константа Ні Обов'язковий

Keyset pagination в 100 разів ефективніша за OFFSET на великих даних.

Оптимізація JOIN: складові індекси

-- Додаємо складовий індекс для типового фільтра CREATE INDEX idx_orders_user_status_created ON orders (user_id, status, created_at DESC); -- Запит використовує index scan без Sort SELECT id, total, status, created_at FROM orders WHERE user_id = $1 AND status = 'completed' ORDER BY created_at DESC LIMIT 10; 

Порядок колонок в індексі: спочатку equality conditions (user_id = $1, status = 'completed'), потім range/sort (created_at DESC).

Налаштування work_mem та LATERAL

Якщо в EXPLAIN ANALYZE бачимо external merge (Disk: ...) при Sort — збільшуємо work_mem для сесії:

SET work_mem = '64MB'; -- Виконуємо важкий аналітичний запит 

У postgresql.conf краще залишити work_mem низьким (4-8MB за замовчуванням) і піднімати для конкретних запитів через SET LOCAL work_mem.

-- LATERAL: для row-dependent subqueries SELECT u.id, u.email, recent.total FROM users u CROSS JOIN LATERAL ( SELECT SUM(total) AS total FROM orders o WHERE o.user_id = u.id AND o.created_at > NOW() - INTERVAL '30 days' ) AS recent; 

LATERAL часто дає кращий план, ніж JOIN на агрегований CTE.

Метрика До оптимізації Після оптимізації
Середній час запиту 1 200 ms 180 ms
Навантаження CPU (середня) 85% 25%
I/O reads за секунду 500 80

Наприклад, оптимізація 15 запитів може заощадити до $1500 на місяць на серверах.

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

  • Аудит 10–15 найважчих запитів через pg_stat_statements та EXPLAIN ANALYZE
  • Переписування запитів з усуненням антипатернів
  • Додавання та налаштування індексів (включаючи складові та часткові)
  • Налаштування параметрів PostgreSQL (shared_buffers, work_mem, effective_cache_size)
  • Надання звіту з вимірами до/після та рекомендаціями для команди
  • Додатково: навчання розробників роботі з планами запитів

Команда має 7+ років досвіду в оптимізації PostgreSQL, понад 50 успішних проектів.

В одному з проєктів ми скоротили час виконання запитів з 4 секунд до 200 мс — це знизило навантаження на сервер та дозволило уникнути upgrade інфраструктури. Оптимізація 15 повільних запитів може вивільнити значну частину ресурсів сервера.

Строки та вартість

Діагностика та оптимізація 10–15 повільних запитів — 2–3 дні. Глибокий аудит схеми та запитів для високонавантаженого застосунку — 3–5 днів. Вартість розраховується індивідуально після оцінки обсягу.

Пропонуємо оптимізацію SQL-запитів під ключ: від діагностики до впровадження за 2–3 дні. Напишіть нам для безкоштовної оцінки вашого проекту. Для старту проєкту зв'яжіться з нами в Telegram або поштою — ми проведемо безкоштовний аналіз перших двох запитів. Замовте діагностику — і отримайте звіт з вимірами до/після. Ми гарантуємо вимірне зниження часу запитів не менш ніж на 30% — результат фіксуємо до та після оптимізації.