PostgreSQL
Background Workers и процессы
PostgreSQL - не монолит. Это оркестр из специализированных процессов, каждый из которых выполняет конкретную роль: один пишет WAL, другой чистит буферы, третий запускает VACUUM, четвёртый считает статистику. Когда производительность падает или база ведёт себя странно - понимание какой процесс за что отвечает помогает найти проблему за минуты, а не за дни.
- **Heroku PostgreSQL:** их SRE runbook для 'база тормозит' начинается с проверки pg_stat_bgwriter. buffers_backend >> buffers_clean означает что bgwriter не справляется - первый шаг к решению без угадывания.
- **Notion (2021):** при incident с медленными запросами обнаружили что autovacuum был отключён на production таблице blocks (кто-то добавил autovacuum_enabled=false 'временно' год назад). Таблица накопила 40% dead tuples. VACUUM ANALYZE вернул производительность через 4 часа.
- **Stripe:** мониторят checkpoint_sync_time как ключевую метрику здоровья дисковой подсистемы. Рост с 50ms до 500ms - ранний сигнал что диск начинает деградировать, за несколько дней до более явных симптомов.
Checkpointer: страж консистентности
Checkpointer - выделенный процесс PostgreSQL, отвечающий за создание checkpoint'ов. До версии 9.2 это была часть bgwriter. Разделение позволило оптимизировать обе задачи независимо.
Checkpointer запускает checkpoint по двум условиям: истёк checkpoint_timeout (default 5min) или WAL вырос до max_wal_size (default 1GB). При checkpoint он записывает все dirty pages из shared_buffers на диск, затем fsync'ит WAL. Запись растянута на checkpoint_completion_target * checkpoint_timeout секунд.
GitLab обнаружили что checkpoint_sync_time регулярно превышал 30 секунд на пиках нагрузки. Причина: HDD с RAID без write-back cache. Переход на NVMe и установка checkpoint_completion_target = 0.9 снизили checkpoint_sync_time до 200-500ms.
checkpoint_completion_target = 0.9 означает что checkpointer распределяет запись dirty pages на 90% интервала между checkpoint'ами. Зачем не записывать всё сразу в начале?
Background Writer: проактивная запись
bgwriter (background writer) проактивно записывает dirty pages на диск между checkpoint'ами. Цель: когда backend-процесс запрашивает свободный буфер, он уже чистый - не нужно тратить время на flush. Это снижает latency пользовательских запросов.
В pg_stat_bgwriter: buffers_backend = 8M, buffers_clean = 500K. Что это означает и как исправить?
WAL Writer: гарантия durability
walwriter периодически записывает WAL-буфер на диск. Без него backend при COMMIT должен был бы сам вызывать fsync WAL - это было бы дорого при высоком TPS. walwriter батчит эти операции.
walwriter вызывает fsync WAL каждые 200ms (wal_writer_delay). Почему COMMIT при synchronous_commit=on не ждёт следующего walwriter цикла?
Autovacuum Launcher: оркестратор уборки
Autovacuum launcher - процесс-оркестратор, запускающий autovacuum worker'ов для таблиц которым нужен VACUUM или ANALYZE. Это центральный компонент самообслуживания PostgreSQL: без него bloat накапливается, статистика устаревает и возможен txid wraparound.
Shopify настраивает autovacuum per-table для каждой таблицы с >10M строк. Для orders (300M+ строк) autovacuum_vacuum_scale_factor = 0.005 (0.5%) вместо default 20% - иначе autovacuum запускается слишком редко и накапливается 60M dead tuples между запусками.
Таблица events имеет 50M строк. При autovacuum_vacuum_scale_factor = 0.2 (default) - сколько dead tuples накопится до запуска autovacuum?
Stats Collector: данные для планировщика
Stats collector собирает статистику активности: сколько строк прочитано, сколько запросов выполнено, сколько dead tuples в каждой таблице. До PostgreSQL 15 это был отдельный процесс; с версии 15 данные хранятся в shared memory напрямую.
ANALYZE обновляет статистику распределения данных в pg_statistic, которую используют планировщик. Autovacuum автоматически запускает ANALYZE при существенных изменениях таблицы. Устаревшая статистика - причина плохих планов: планировщик думает что в таблице 1000 строк, а их 10 миллионов.
Остановка autovacuum на время большой нагрузки ускорит систему
Краткосрочный выигрыш от отключения autovacuum оборачивается долгосрочными потерями: bloat растёт, статистика устаревает, планировщик делает плохие планы, txid age растёт к wraparound. Если autovacuum мешает - нужно тюнить его параметры (cost_delay, cost_limit), а не отключать.
autovacuum throttled по умолчанию через vacuum_cost_delay и vacuum_cost_limit - он намеренно уступает место рабочим запросам. Если он всё равно создаёт нагрузку - значит накоплен такой bloat что без агрессивной уборки не обойтись. Отключение усугубит ситуацию.
В pg_stat_user_tables для таблицы products: seq_scan = 50000, idx_scan = 100. Что это означает?
Итоги
- **5 ключевых фоновых процессов:** checkpointer (checkpoint'ы), bgwriter (проактивная запись dirty pages), walwriter (flush WAL буфера), autovacuum launcher (оркестр VACUUM/ANALYZE), stats collector (метрики для планировщика и мониторинга)
- **pg_stat_bgwriter - диагностический центр:** buffers_backend >> buffers_clean означает bgwriter не справляется; checkpoints_req >> checkpoints_timed означает max_wal_size мал
- **Autovacuum нельзя отключать:** тюнить - да, через per-table параметры. Но отключение ведёт к bloat, устаревшей статистике и wraparound
Связанные темы
Фоновые процессы управляют всеми слоями PostgreSQL:
- WAL и Durability — walwriter обслуживает WAL-буфер; checkpointer создаёт checkpoint'ы и тесно взаимодействует с WAL-механизмом для определения точки восстановления
- Buffer Cache и Shared Buffers — bgwriter и checkpointer пишут dirty buffers из shared_buffers на диск. Без них buffer manager вынужден делать это в критический момент запроса
- VACUUM и bloat — autovacuum launcher и его workers - это автоматизированный VACUUM. Понимание когда и как они запускаются критично для управления bloat
Вопросы для размышления
- Если autovacuum_max_workers = 3 и все три заняты vacuum'ом трёх больших таблиц - что происходит с другими таблицами которым тоже нужен vacuum? Как это влияет на txid wraparound?
- Как бы выглядел идеальный dashboard для мониторинга фоновых процессов PostgreSQL? Какие метрики из pg_stat_bgwriter и pg_stat_user_tables включить в первую очередь?
- bgwriter_delay = 200ms означает что между итерациями bgwriter проходит 200ms. При 5000 write TPS и страницах по 8KB - сколько dirty pages может накопиться между итерациями? Хватит ли bgwriter_lru_maxpages = 100 чтобы успевать?