Тюнинг производительности 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
    1214
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Тюнинг производительности 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 sort node, он может занять 3 × work_mem. При 100 параллельных соединениях с тяжёлыми запросами потребление может быть 100 × 3 × work_mem.

effective_cache_size — не реально выделяемая память, а подсказка планировщику о том, сколько памяти доступно для кэширования OS + shared_buffers. Влияет на выбор между index scan и seq scan.

maintenance_work_mem — для VACUUM, CREATE INDEX, ALTER TABLE. Не влияет на обычные запросы.

Рекомендуемые значения

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

# postgresql.conf

# 25% RAM для выделенного сервера
shared_buffers = 8GB

# Остаток RAM за вычетом shared_buffers и системы
effective_cache_size = 24GB  # shared_buffers + OS page cache

# Осторожно: work_mem умножается на число параллельных операций
# Для OLTP с короткими запросами: 4-16 MB
# Для аналитики с GROUP BY / ORDER BY: 64-256 MB
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

# Checkpoint происходит реже — меньше 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 в transaction mode: 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.