Тюнінг продуктивності PostgreSQL (shared_buffers, work_mem, effective_cache_size)

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

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

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

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Тюнінг продуктивності PostgreSQL (shared_buffers, work_mem, effective_cache_size)
Складна
~2-3 робочих дні
Часті питання

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

Етапи розробки

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

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

Тюнінг продуктивності PostgreSQL (shared_buffers, work_mem, effective_cache_size)

Стандартна конфігурація PostgreSQL — shared_buffers = 128MB, work_mem = 4MB — написана для запуску на будь-якому залізі 2005 року. На сучасному сервері з 32–128 ГБ ОЗУ вона залишає 95% пам'яті невикористаною. Правильний тюнінг — це не просто «встановити більші цифри», а розуміння того, як PostgreSQL використовує пам'ять і взаємодіє з ОС.

Як PostgreSQL використовує пам'ять

shared_buffers — спільний кеш сторінок бази даних для всіх процесів. Сторінки, які PostgreSQL прочитав з диска, потрапляють сюди. Якщо сторінка в shared_buffers — наступний запит отримує її з пам'яті.

work_mem — пам'ять для кожної операції сортування або hash join у межах одного запиту. Якщо запит має 3 вузли сортування, він може займати 3 × work_mem. При 100 паралельних з'єднаннях з важкими запитами споживання може бути 100 × 3 × work_mem.

effective_cache_size — не реально виділена пам'ять, а підказка планувальнику про те, скільки пам'яті доступно для кеширування (ОС + shared_buffers). Впливає на вибір між index scan та seq scan.

maintenance_work_mem — для VACUUM, CREATE INDEX, ALTER TABLE. Не впливає на звичайні запити.

Рекомендовані значення

Для виділеного сервера PostgreSQL з 32 ГБ ОЗУ:

# postgresql.conf

# 25% ОЗУ для виділеного сервера
shared_buffers = 8GB

# Залишок ОЗУ мінус shared_buffers та система
effective_cache_size = 24GB  # shared_buffers + OS page cache

# Увага: work_mem множиться на кількість паралельних операцій
# Для OLTP з короткими запитами: 4-16 МБ
# Для аналітики з GROUP BY / ORDER BY: 64-256 МБ
work_mem = 16MB

# Для VACUUM та CREATE INDEX — збільшувати тільки під час обслуговування
maintenance_work_mem = 2GB

# WAL буфер — 64 МБ достатньо для більшості навантажень
wal_buffers = 64MB

Інструмент pgtune (https://pgtune.leopard.in.ua) генерує стартові значення за типом навантаження (OLTP, DW, Web).

Налаштування планувальника

# Модель вартості для SSD (за замовчуванням розраховується на HDD)
random_page_cost = 1.1    # SSD: 1.1, HDD: 4.0 (за замовчуванням)
seq_page_cost    = 1.0    # звичайно не змінюють

# Включити паралельні запити (PostgreSQL 9.6+)
max_parallel_workers_per_gather = 4   # до кількості CPU cores
max_parallel_workers = 8
parallel_tuple_cost  = 0.1
parallel_setup_cost  = 1000.0

# Статистика: більше = кращий планувальник вибирає індекси
default_statistics_target = 100  # за замовчуванням 100, для складних стовпців — до 500

Для конкретних стовпців з перекошеним розподілом даних:

ALTER TABLE orders ALTER COLUMN status SET STATISTICS 500;
ANALYZE orders;

Checkpoint та WAL

# Контрольні точки трапляються рідше — менше спайків I/O
checkpoint_completion_target = 0.9  # розтягнути checkpoint на 90% інтервалу
checkpoint_timeout = 15min          # максимальний інтервал (за замовчуванням 5min)
max_wal_size = 4GB                  # при інтенсивному записі — збільшити

# fsync вимикати НЕМОЖНА у продакшені (втрата даних при відключенні живлення)
fsync = on

# synchronous_commit = off — дозволити втрату до 60ms транзакцій
# Доречно для некритичних даних, не для фінансових
synchronous_commit = on

Тюнінг для конкретного випадку: сортування

Запит повільно виконує ORDER BY на великій таблиці — сортування відбувається через тимчасовий файл на диску:

-- Перевіряємо: чи є temp files у запиті
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT)
SELECT * FROM events
WHERE user_id = 1
ORDER BY created_at DESC
LIMIT 100;

-- Якщо у виводі "Sort Method: external merge  Disk: 45678kB" — потрібен індекс або більше work_mem
-- Створюємо покриваючий індекс
CREATE INDEX CONCURRENTLY idx_events_user_date
  ON events(user_id, created_at DESC)
  INCLUDE (id, event_type, payload);
-- INCLUDE додає дані в індекс, запит працює без звернення до таблиці

Моніторинг hit rate

-- Ефективність shared_buffers: має бути > 99% для OLTP
SELECT
    sum(heap_blks_hit)  AS heap_hit,
    sum(heap_blks_read) AS heap_read,
    round(
        sum(heap_blks_hit)::numeric /
        nullif(sum(heap_blks_hit) + sum(heap_blks_read), 0) * 100,
        2
    ) AS cache_hit_ratio
FROM pg_statio_user_tables;

-- Аналогічно для індексів
SELECT
    round(
        sum(idx_blks_hit)::numeric /
        nullif(sum(idx_blks_hit) + sum(idx_blks_read), 0) * 100,
        2
    ) AS index_hit_ratio
FROM pg_statio_user_indexes;

Якщо cache_hit_ratio < 99% — або shared_buffers малий, або робочий набір даних не вміщується в пам'ять вообще.

Розширення pg_buffercache

CREATE EXTENSION pg_buffercache;

-- Що зараз знаходиться в shared_buffers?
SELECT c.relname,
       count(*) AS buffers,
       round(count(*) * 8192.0 / 1024 / 1024, 1) AS size_mb,
       round(count(*) * 100.0 / (SELECT count(*) FROM pg_buffercache), 1) AS pct
FROM pg_buffercache b
JOIN pg_class c ON b.relfilenode = c.relfilenode
GROUP BY c.relname
ORDER BY buffers DESC
LIMIT 20;

Параметри з'єднань

max_connections = 200   # для прямих з'єднань; з PgBouncer можна менше

# Розмір стека для кожного backend-процесу
# Не зменшувати нижче 100KB
# Зменшення max_stack_depth не рекомендується

При використанні PgBouncer у режимі транзакцій: max_connections = 50-100 на PostgreSQL (реальні бекенди), тисячі клієнтів підключаються до PgBouncer.

Порядок застосування змін

Параметр Потребує перезавантаження
shared_buffers Так
max_connections Так
work_mem Ні (RELOAD)
effective_cache_size Ні
checkpoint_timeout Ні
random_page_cost Ні
max_parallel_workers Ні
-- Застосувати без перезавантаження
SELECT pg_reload_conf();

-- Перевірити, що зміна вступила в силу
SHOW work_mem;

Профіль навантаження впливає на тюнінг

Web OLTP (багато коротких транзакцій, INSERT/SELECT за PK): малий work_mem (4–16 МБ), великий shared_buffers, max_connections через PgBouncer.

Аналітичні запити (GROUP BY, window functions, великі JOIN): великий work_mem (256 МБ – 1 ГБ), max_parallel_workers_per_gather = 4+, можна зменшити max_connections.

Змішане навантаження: PgBouncer для OLTP-з'єднань + окрема реплика для аналітики з іншим work_mem.