2.1 Один главный, много рабочих
Когда сервер запускается, первым стартует postmaster - главный процесс. Сам он данные не обслуживает. Его дело - слушать порт, и на каждое новое соединение порождать рабочий процесс. Он же запускает фоновые процессы и приглядывает за всеми: если кто-то упал, postmaster это заметит.
Рабочий процесс соединения называют backend. Он обслуживает ровно одно клиентское соединение: разбирает запросы, планирует, исполняет, отдаёт результат. Закрылось соединение - backend завершился.
2.2 Кто все эти процессы
Кроме backend'ов соединений, постоянно работают фоновые процессы. Каждый делает одну узкую работу:
| Процесс | Чем занят |
|---|---|
| checkpointer | сбрасывает накопленные изменения на диск контрольными точками |
| background writer | заранее выталкивает грязные страницы из кеша, чтобы backend'ам было куда читать |
| walwriter | пишет журнал предзаписи на диск |
| autovacuum launcher | решает, какую таблицу пора чистить, и запускает worker'ов |
| logical replication launcher | обслуживает логическую репликацию |
Под нагрузкой к ним добавляются временные: autovacuum worker'ы (сама очистка), параллельные worker'ы (для тяжёлых запросов), walsender и walreceiver (репликация). Но костяк - те пять, что в таблице.
Названия процессов - не случайные ярлыки. Каждый соответствует механизму, которому в книге посвящена глава: checkpointer и walwriter - журналу, autovacuum - очистке, background writer - буферному кешу.
2.3 Увидеть процессы из SQL
Не обязательно лезть в ps: PostgreSQL показывает свои процессы сам.
Представление pg_stat_activity содержит строку на каждый процесс, а
колонка backend_type говорит, что это за процесс.
SELECT pid, backend_type FROM pg_stat_activity ORDER BY backend_type;
-- pid | backend_type
-- -------+------------------------------
-- ... | autovacuum launcher
-- ... | background writer
-- ... | checkpointer
-- ... | client backend <- это твоё соединение
-- ... | logical replication launcher
-- ... | walwriter
Твоё собственное соединение - это client backend. Остальные строки -
фоновые процессы. Их обычно ровно пять на свежем сервере. Подсчитать
фоновые можно так:
SELECT count(*) FROM pg_stat_activity WHERE backend_type <> 'client backend';
-- 5
2.4 Разделяемая память: общий стол
Раз процессов много, а данные общие, должно быть место, которое они видят все сразу. Это разделяемая память (shared memory) - область, к которой при старте подключается каждый процесс сервера.
В ней живёт самое важное общее хозяйство: буферный кеш страниц
(shared_buffers), список активных транзакций, блокировки. Когда один
backend изменил страницу в буферном кеше, другой backend видит это
изменение - они смотрят в одну и ту же память.
SHOW shared_buffers; -- размер общего буферного кеша
Помимо общей, у каждого backend'а есть и частная память под свои
нужды - например, work_mem на сортировки и хеши одного запроса. Эта
память приватна: соседний процесс её не видит. Разделение «общее против
приватного» ещё всплывёт, когда будем настраивать память под нагрузку.
2.5 Подводный камень: соединения не бесплатны
Модель «процесс на соединение» красива, но у неё есть цена. Каждое соединение - это процесс операционной системы: своя память, своё место в планировщике ОС, свой кусок в общих структурах. Тысяча открытых соединений - это тысяча процессов, даже если 990 из них просто простаивают.
Хуже того, простаивающие соединения утяжеляют работу остальным: построение снимка данных обходит список всех процессов, и чем их больше, тем дороже каждый снимок (об этом - в части про MVCC).
Поэтому типичный совет - не открывать соединения сотнями из приложения, а ставить пул соединений (pgbouncer и подобные): небольшое число реальных соединений к серверу, которые приложение переиспользует. Это не каприз, а прямое следствие процессной модели.
Уроки в sandbox
lab-2.1. Первый коннект и карта процессов
Подключись к свежей базе и составь карту её процессов: кто работает в
фоне и за что отвечает. Сначала предскажи, сколько фоновых процессов
увидишь и какие, потом сверь с pg_stat_activity.
Подключись к базе
lab(psql уже настроен) и убедись, что соединение живо:SELECT 1;.Предскажи: сколько строк вернёт
pg_stat_activityи какая из них - твоя? Запиши догадку.Выполни
SELECT pid, backend_type FROM pg_stat_activity ORDER BY backend_type;и найди свойclient backend.Сосчитай фоновые процессы:
SELECT count(*) FROM pg_stat_activity WHERE backend_type <> 'client backend';- предскажи число до запуска.Сопоставь каждый
backend_typeс его работой по таблице из главы: checkpointer, background writer, walwriter, autovacuum launcher.Посмотри размер общего кеша:
SHOW shared_buffers;- это память, которую видят все эти процессы сразу.
sandbox с автопроверкой - открыть в песочнице
Резюме
- postmaster - главный процесс: слушает порт, порождает backend'ы и фоновые процессы, следит за ними.
- backend - процесс одного клиентского соединения; живёт, пока открыто соединение.
- Фоновый костяк: checkpointer, background writer, walwriter, autovacuum launcher, logical replication launcher.
- Процессы видны из SQL через pg_stat_activity.backend_type, не только через ps.
- Разделяемая память (shared_buffers и пр.) - общий стол: изменения в ней видят все процессы сервера.
- Модель «процесс на соединение» делает соединения дорогими, поэтому нужен пул соединений.
Контрольные вопросы
Чем занят postmaster и почему он сам не обслуживает запросы?
Показать ответ
postmaster - главный процесс сервера. Он слушает сетевой порт, на каждое новое соединение порождает отдельный backend-процесс, запускает фоновые процессы и присматривает за всеми (перезапускает при сбое). Сами запросы он не обслуживает: это работа backend'ов. Такое разделение делает сбой одного соединения локальным - падает его backend, а сервер продолжает работать.
Как из SQL отличить своё соединение от фоновых процессов?
Показать ответ
Через
pg_stat_activity: колонкаbackend_typeу твоего соединения -client backend, а у фоновых процессов другие значения (checkpointer,background writer,walwriter,autovacuum launcherи т.д.). Своё соединение можно ещё точно опознать поpid = pg_backend_pid().Зачем нужна разделяемая память и что в ней лежит?
Показать ответ
Процессов в PostgreSQL много, а данные у них общие, поэтому нужна память, которую видят все сразу. В разделяемой памяти лежит буферный кеш страниц (
shared_buffers), список активных транзакций, блокировки. Когда один backend меняет страницу в кеше, остальные видят изменение, потому что смотрят в одну и ту же область. У каждого backend'а есть и приватная память (например,work_mem), которую соседи не видят.Почему держать тысячи открытых соединений - плохая идея?
Показать ответ
Каждое соединение - это отдельный процесс ОС со своей памятью и местом в планировщике, даже когда оно простаивает. Тысячи процессов - заметная нагрузка сами по себе. Вдобавок построение снимка данных обходит список всех активных процессов, поэтому много соединений удорожают снимки всем остальным. Решение - пул соединений, который держит небольшое число реальных соединений и переиспользует их.
Почему имена фоновых процессов стоит запомнить?
Показать ответ
Потому что каждое имя - это указатель на механизм, которому посвящена отдельная глава. checkpointer и walwriter ведут к журналу предзаписи, autovacuum - к очистке мусора от старых версий строк, background writer - к буферному кешу. Увидев процесс, ты сразу знаешь, какая подсистема сейчас работает и где про неё прочитать.