Архитектура PostgreSQL: процессы и память

Рейтинг: 78.5% · 14 голосов
Подробный курс по PostgreSQL: SQL по демобазе, типы данных и JSONB, индексы, транзакции и MVCC, VACUUM, EXPLAIN и планировщик, роли и привилегии, репликация, бэкап, настройка под нагрузку и под 1С. Актуально на 2026.
Ответить
Аватара пользователя
Egor_DBA
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Архитектура PostgreSQL: процессы и память

Сообщение Egor_DBA »

Оглавление курса (47)
  1. Что такое PostgreSQL: история, философия, где применяется
  2. Урок 1. Что нового в PostgreSQL 15 и далее (15 -> 16 -> 17)
  3. Установка и первый запуск: Linux, Windows, Docker
  4. psql детально: метакоманды и работа в консоли
  5. Демобаза Авиаперевозки Postgres Pro: структура и развёртывание
  6. SELECT: проекция, фильтрация, сортировка
  7. Транзакции и ACID
  8. Уровни изоляции транзакций: Read Committed, Repeatable Read, Serializable
  9. Соединения таблиц (JOIN)
  10. Агрегация: GROUP BY, HAVING, GROUPING SETS
  11. Подзапросы и CTE (WITH), рекурсия
  12. Оконные функции
  13. Изменение данных: INSERT/UPDATE/DELETE, UPSERT, MERGE
  14. Числовые и булевы типы, NULL и трёхзначная логика
  15. Строки, текст, дата и время, таймзоны
  16. Массивы, диапазоны, enum, составные типы
  17. JSON и JSONB: операторы и индексация
  18. DDL: CREATE TABLE, схемы, ALTER
  19. Ограничения целостности
  20. Представления и материализованные представления
  21. Последовательности, IDENTITY, генерируемые столбцы
  22. Индексы: B-tree и когда индекс не используется
  23. Типы индексов: Hash, GiST, SP-GiST, GIN, BRIN
  24. Продвинутые индексы: частичные, по выражению, покрывающие
  25. Обслуживание индексов и таблиц: bloat, REINDEX, CONCURRENTLY
  26. Полнотекстовый поиск
  27. Роли и пользователи
  28. Привилегии: GRANT/REVOKE
  29. Row-Level Security и безопасность данных
  30. Подключение приложения: pg_hba.conf, SSL, пулы соединений
  31. Серверное программирование: функции и процедуры
  32. Триггеры и события
  33. Архитектура PostgreSQL: процессы и память (вы здесь)
  34. MVCC: версии строк и видимость
  35. VACUUM, autovacuum, freeze и wraparound
  36. WAL, контрольные точки и долговечность
  37. Блокировки и взаимоблокировки
  38. Планировщик запросов
  39. EXPLAIN и EXPLAIN ANALYZE: чтение планов
  40. Конфигурация сервера: ключевые параметры
  41. Настройка под нагрузку и под 1С
  42. Параллельные запросы и партиционирование
  43. Резервное копирование и восстановление
  44. Репликация: потоковая и hot standby
  45. Логическая репликация и кластерные решения
  46. Внешние данные, расширения и сертификация
  47. Разбор планов запросов на explain.tensor.ru: глубокое чтение EXPLAIN ANALYZE
Урок 32. Архитектура PostgreSQL: процессы и память

В этом уроке разбираем, из чего реально состоит работающий сервер postgresql: какие операционные процессы крутятся в фоне, кто обслуживает ваше соединение и куда уходит память. Без этой картины настройка shared_buffers и work_mem превращается в гадание, а строчки в выводе ps или в pg_stat_activity выглядят как магия. Цель - чтобы вы смотрели на запущенный кластер и понимали, кто за что отвечает и почему сервер ведёт себя именно так под нагрузкой.

Изображение

Как это работает

PostgreSQL устроен не как один большой многопоточный демон, а как семья отдельных процессов операционной системы. Главный из них - postmaster (он же supervisor). Он слушает сетевой порт, принимает входящие подключения и на каждое новое соединение порождает отдельный дочерний процесс - backend. Сам postmaster запросы не обслуживает: его задача - принимать соединения, поднимать фоновые процессы и перезапускать упавшие.

Один клиент - один backend. Это важная мысль. Backend живёт ровно столько, сколько открыт ваш сеанс, выполняет ваши команды и умирает при отключении. Отсюда два следствия. Первое: соединение в PostgreSQL дорогое, это полноценный процесс ОС с собственной памятью, поэтому тысячи коротких подключений лучше пропускать через пул (PgBouncer), а не открывать напрямую. Это особенно касается postgresql для 1с и веб-приложений, где соединения плодятся быстро. Второе: между процессами нужна общая память, чтобы они видели одни и те же данные.

Эту роль играет разделяемая память (shared memory) - область, которую postmaster выделяет при старте и к которой подключаются все backend и фоновые процессы. Самая большая её часть - shared_buffers, кэш страниц таблиц и индексов. Когда backend читает строку, он не лезет каждый раз в файл: сначала ищет нужную 8-килобайтную страницу в shared_buffers. Если её там нет, страница подтягивается с диска (а точнее, чаще из кэша операционной системы) и кладётся в буфер. Получается двойное кэширование: shared_buffers внутри Postgres и page cache самой ОС. Поэтому отдавать под shared_buffers всю RAM бессмысленно - вы просто задвоите кэш и отнимете память у ОС.

Теперь фоновые процессы, ради которых сервер вообще держится живым.

checkpointer периодически сбрасывает на диск грязные (изменённые) страницы из shared_buffers и ставит контрольную точку - отметку, с которой можно начать восстановление после сбоя. bgwriter (background writer) помогает ему, заранее выталкивая грязные страницы, чтобы backend не упирался в чистый буфер в самый неподходящий момент. walwriter сбрасывает на диск журнал предзаписи WAL - тот самый журнал, благодаря которому БД переживает падение питания. autovacuum (его launcher и рабочие worker-ы) в фоне чистит мёртвые версии строк после UPDATE и DELETE и обновляет статистику для планировщика. Ещё есть startup-процесс (восстановление при запуске), архиватор WAL, логгер и процессы логической репликации.

Кроме разделяемой памяти у каждого backend есть локальная (приватная) память. Главный её параметр - work_mem. Это лимит на одну операцию сортировки или хеширования внутри запроса. Ключевое слово - на операцию: сложный запрос с несколькими сортировками и хеш-соединениями может занять несколько work_mem сразу, а если таких backend много, суммарный аппетит умножается. Если операции не хватает work_mem, она проливается на диск во временные файлы и резко тормозит. Отдельно стоит maintenance_work_mem - память для тяжёлых служебных операций (CREATE INDEX, VACUUM), её можно ставить заметно крупнее, потому что такие операции редки и обычно идут не параллельно.

Что поменялось в свежих версиях. В PostgreSQL 16 фоновую запись и обработку WAL продолжили оптимизировать, а логическую репликацию научили работать со standby. В PostgreSQL 17 переписали механизм vacuum: вместо линейного массива идентификаторов мёртвых строк, ограниченного maintenance_work_mem, появилось компактное TID-хранилище, так что чистка крупных таблиц стала экономнее по памяти и реже ходит по индексам в несколько проходов. База у нас - PG 15, но эти отличия стоит держать в голове при апгрейде.

SQL и примеры

Посмотрим на живой сервер. Кто сейчас подключён и какие backend активны:

Код: Выделить всё

SELECT pid, usename, application_name, state, backend_type
FROM pg_stat_activity
ORDER BY backend_type, pid;
Запрос показывает все процессы: обычные client backend (ваши сеансы) и фоновые - checkpointer, background writer, walwriter, autovacuum launcher. Колонка backend_type как раз называет роль процесса.

Сколько памяти отдано под разделяемый кэш и каков размер буфера:

Код: Выделить всё

SHOW shared_buffers;
SHOW work_mem;
SHOW maintenance_work_mem;
SELECT current_setting('block_size');  -- обычно 8192 байта
Заглянем, что лежит в shared_buffers прямо сейчас (нужно расширение):

Код: Выделить всё

CREATE EXTENSION IF NOT EXISTS pg_buffercache;

SELECT c.relname,
       count(*) AS buffers,
       pg_size_pretty(count(*) * 8192) AS in_cache
FROM pg_buffercache b
JOIN pg_class c ON c.relfilenode = b.relfilenode
GROUP BY c.relname
ORDER BY buffers DESC
LIMIT 10;
Здесь видно, какие таблицы и индексы реально греют кэш. Прогоните на демобазе bookings тяжёлый отчёт по рейсам - и эти таблицы всплывут наверх:

Код: Выделить всё

SELECT f.flight_no, count(*) AS passengers
FROM bookings.flights f
JOIN bookings.ticket_flights tf ON tf.flight_id = f.flight_id
JOIN bookings.boarding_passes bp ON bp.ticket_no = tf.ticket_no
                                  AND bp.flight_id = tf.flight_id
GROUP BY f.flight_no
ORDER BY passengers DESC
LIMIT 5;
После этого запроса страницы flights, ticket_flights и boarding_passes окажутся в shared_buffers, и повторный прогон пойдёт заметно быстрее - данные уже в кэше. Через EXPLAIN (ANALYZE, BUFFERS) на этом же запросе можно увидеть строки shared hit (попадание в кэш) против shared read (чтение с диска).

Частые грабли
  • Ставят shared_buffers в 80-90% RAM. ОС тоже кэширует файлы БД, память задваивается, а реальной памяти под работу backend не остаётся. Разумная стартовая точка - около четверти RAM, дальше по метрикам.
  • Считают work_mem лимитом на запрос или на сеанс. Это лимит на одну операцию. Сотня соединений с несколькими сортировками легко вынесет сервер в OOM.
  • Открывают тысячи прямых соединений из приложения. Каждое - отдельный процесс с памятью и накладными расходами postmaster. Без пула (PgBouncer) сервер захлёбывается на ровном месте.
  • Думают, что один зависший backend можно убить kill -9. Жёсткое убийство backend роняет весь кластер в аварийное восстановление. Корректно - pg_terminate_backend(pid).
  • Путают bgwriter и checkpointer. Первый сглаживает запись грязных страниц, второй ставит точки восстановления; настраиваются они разными параметрами.
  • Поднимают maintenance_work_mem гигантским при включённом параллельном autovacuum: несколько worker-ов умножают эту память. В PG 16 и старше это менее болезненно, в PG 17 vacuum экономнее по памяти.
Мини-лаба
  • Запустите psql и выполните SELECT backend_type, count(*) FROM pg_stat_activity GROUP BY backend_type. Найдите checkpointer, walwriter, background writer.
  • В другом терминале посмотрите процессы ОС: ps aux | grep postgres. Сопоставьте их с тем, что показал pg_stat_activity.
  • Выведите текущие значения shared_buffers, work_mem, maintenance_work_mem через SHOW.
  • Установите CREATE EXTENSION pg_buffercache и сделайте запрос топ-10 объектов в кэше на пустой нагрузке.
  • Прогоните отчёт по boarding_passes из примера выше, затем снова посмотрите pg_buffercache - таблицы должны подняться в топ.
  • Сделайте SET work_mem = '64kB' в своём сеансе, выполните большой ORDER BY и через EXPLAIN (ANALYZE) поймайте строку про external merge Disk - сортировка пролилась на диск.
  • Верните work_mem к нормальному значению и сравните время выполнения того же запроса.
Контрольные вопросы
  • Чем занимается postmaster и почему он сам не выполняет SQL-запросы клиентов?
  • Почему один backend на соединение делает прямые подключения дорогими и чем здесь помогает пул соединений?
  • В чём разница между shared_buffers и кэшем операционной системы и почему вредно отдавать под shared_buffers почти всю RAM?
  • work_mem - это лимит на запрос, на сеанс или на операцию? Как это влияет на риск нехватки памяти?
  • За что отвечают checkpointer, bgwriter и walwriter и чем их роли различаются?
  • Что изменилось в управлении памятью vacuum в PostgreSQL 17 по сравнению с PG 15?
👍2 ❤️1 🔥1 😄 🤔1
Аватара пользователя
hv8swf0h
Сообщения: 1
Зарегистрирован: 25 май 2026, 09:04

Re: Архитектура PostgreSQL: процессы и память

Сообщение hv8swf0h »

а pg_terminate_backend и pg_cancel_backend это разное? на проде запутался какой когда дергать
👍 ❤️ 🔥 😄 🤔
Аватара пользователя
tel0815
Сообщения: 1
Зарегистрирован: 23 май 2026, 02:02

Re: Архитектура PostgreSQL: процессы и память

Сообщение tel0815 »

проверил pg_buffercache на своей базе для 1с, top по буферам забит индексами, а не таблицами. это нормально или я что-то накрутил с work_mem?
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Триггеры и события
Следующая глава →
MVCC: версии строк и видимость

Все главы курса «PostgreSQL: от первого запроса до продакшена»

Поделиться темой: ✈ Telegram VK
Похожие запросы: autovacuum не успевает как настроить postgresqlкак читать план запроса explain analyze в postgresqlpostgresql не использует индекс делает seq scanтриггеры и хранимые процедуры pl/pgsql в postgresqlбэкап и восстановление postgresql через pg_dump

Вернуться в «PostgreSQL: от первого запроса до продакшена»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость