Архитектура PostgreSQL и внутреннее устройство
Раздел 8.11, шаг 1 из 12. Дальше — оптимизация и индексы.
Зачем знать внутренности
PostgreSQL на первый взгляд ведёт себя как «ещё одна SQL-база», но многие production-инциденты связаны именно с механизмами ядра — раздуванием таблиц (bloat), задержкой autovacuum, исчерпанием идентификаторов транзакций (wraparound) или неправильным пониманием WAL. Без этой базы тюнинг postgresql.conf и выбор стратегии бэкапов остаются угадыванием.
Общая теория транзакций и журналов — в главе 8 основ БД; здесь — как это реализовано в Postgres.
Процессная модель
После запуска postgres (postmaster) создаёт отдельный OS-процесс на каждое клиентское соединение — backend. Это дорого по памяти и файловым дескрипторам, поэтому в production почти всегда ставят PgBouncer (шаг 6).
| Процесс | Назначение |
|---|---|
| postmaster | Принимает соединения, форкает backend, перезапускает упавшие фоновые процессы |
| backend | Выполняет SQL одного клиента |
| checkpointer | Периодически сбрасывает «грязные» страницы из shared buffers на диск, создаёт checkpoint в WAL |
| background writer (bgwriter) | Заранее выталкивает изменённые страницы, снижая пиковую нагрузку checkpoint |
| walwriter | Группирует запись WAL на диск |
| autovacuum launcher | Запускает worker-процессы VACUUM/ANALYZE |
| stats collector | Собирает статистику для pg_stat_* |
MVCC — многоверсионность
MVCC (Multi-Version Concurrency Control) позволяет читателям не блокировать писателей и наоборот. При UPDATE PostgreSQL не перезаписывает строку на месте — создаёт новую версию строки, а старую помечает «мёртвой» для текущих транзакций.
Каждая версия строки несёт служебные поля (системные колонки):
| Поле | Смысл |
|---|---|
| xmin | ID транзакции, создавшей версию |
| xmax | ID транзакции, удалившей/заменившей версию (0 = активна) |
| ctid | Физический адрес версии (block, offset) |
| tableoid | OID таблицы (полезно в наследовании/партициях) |
Проверить системные поля можно так:
SELECT xmin, xmax, ctid, *
FROM orders
WHERE id = 42;
Транзакция видит только те версии, которые видимы в её снимке (snapshot).
XID и снимки данных
XID (Transaction ID) — 32-битный счётчик транзакций. Каждая транзакция получает XID при первой записи в БД.
Snapshot фиксирует для транзакции:
- какие XID уже завершились (committed);
- какие ещё в процессе (in-progress);
- горизонт — минимальный XID среди активных транзакций.
Правило видимости (упрощённо): версия строки видна, если xmin committed до снимка и (xmax пуст или aborted, или xmax committed после снимка).
Долгие транзакции (открытый idle-in-transaction) удерживают horizon и мешают VACUUM удалять старые версии — типичный источник bloat.
VACUUM и autovacuum
VACUUM — сборщик «мусора» MVCC:
- помечает мёртвые версии как переиспользуемое место в страницах;
- обновляет visibility map (ускоряет index-only scan);
- защищает от transaction ID wraparound (см. ниже);
- при
VACUUM FULL— переписывает таблицу целиком (блокирует таблицу, освобождает место ОС).
Autovacuum запускает VACUUM автоматически, когда число мёртвых строк превышает порог:
threshold = autovacuum_vacuum_threshold + autovacuum_vacuum_scale_factor × reltuples
Проверка bloat и autovacuum:
SELECT relname, n_live_tup, n_dead_tup, last_autovacuum, last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;
Bloat — когда физический размер таблицы/индекса сильно больше «полезных» данных из-за накопившихся мёртвых версий и фрагментации. Симптомы — медленные seq scan, раздутая БД на диске при малом объёме логических данных. Лечение — настроить autovacuum, убрать долгие транзакции; в крайних случаях pg_repack или VACUUM FULL в окно обслуживания.
Wraparound — зацикливание XID
XID — 32-бита (~4 млрд значений). PostgreSQL использует кольцевую арифметику: «старые» XID считаются «в будущем» относительно текущего.
Если autovacuum не успевает «заморозить» (freeze) старые строки, БД может перейти в режим anti-wraparound vacuum и в конце — отказ в новых транзакциях, пока freeze не завершится.
Мониторинг:
SELECT datname, age(datfrozenxid) AS xid_age
FROM pg_database
ORDER BY xid_age DESC;
Порог тревоги — обычно age > 200 млн (из 2 млрд до wraparound). Для таблиц — pg_class.relfrozenxid.
Shared Buffers и WAL
Shared buffers — кэш страниц данных (8 KB блоки) в разделяемой памяти всех процессов. Чтение идёт через буфер; при промахе — чтение с диска.
WAL (Write-Ahead Log) — append-only журнал логических изменений. Правило WAL: изменения сначала попадают в WAL и сбрасываются на диск, потом (лениво) — dirty-страницы в data files.
| Компонент | Роль |
|---|---|
| WAL buffer | Буферизация записи WAL в памяти |
| walwriter | Сброс WAL buffer на диск |
| checkpointer | Checkpoint — точка, с которой crash recovery короче |
| bgwriter | Плавная запись dirty pages между checkpoint |
При падении питания PostgreSQL восстанавливает состояние, проигрывая WAL после последнего checkpoint (теория). Отсюда — требования к PITR-бэкапам (шаг 10).
Что PostgreSQL даёт сверх «базового SQL»
Стандарт SQL покрывает запросы и DDL; Postgres добавляет на уровне ядра и экосистемы:
| Возможность | Кратко |
|---|---|
| MVCC без блокировки читателей | см. выше |
| Rich types | массивы, JSONB, UUID, геометрия (PostGIS) |
| Расширения | CREATE EXTENSION — citext, pg_trgm, … |
| Процедурные языки | PL/pgSQL, PL/Python (шаг 5) |
| LISTEN/NOTIFY | pub/sub внутри БД |
| Logical replication | репликация на уровне изменений |
| Foreign Data Wrappers | запросы к внешним источникам |
Практика в lab
- Создайте таблицу, выполните серию
UPDATEодной строки, смотрите ростn_dead_tupвpg_stat_user_tables. - Откройте транзакцию
BEGIN; SELECT …и не коммитьте — в другой сессии обновите те же строки, проверьте autovacuum. SELECT pg_current_wal_lsn();до и после массовой записи — рост LSN показывает объём WAL.
Связанные материалы
- Конфигурация postgresql.conf —
shared_buffers,wal_*, autovacuum. - Блокировки в PostgreSQL — когда MVCC недостаточно.
- Транзакции — TCL в SQL-разделе.
- phpPgAdmin — SQL-вкладка — проверка
xmin,pg_stat_user_tablesиз браузера. - Восстановление после сбоя — теория WAL.
См. также
Другие статьи этого же раздела в боковом меню (как на странице "О разделе"). pg_dump, pg_basebackup, PITR, pg_probackup, Wal-G, стратегия 3-2-1, восстановление в Kubernetes и после failover. pg_stat_statements, pg_stat_activity, auto_explain, pgBadger, Prometheus postgres_exporter, типовые метрики SLA и алерты. Ansible role для установки Postgres, шаблоны postgresql.conf, Terraform для RDS и managed PostgreSQL, GitOps паттерны для инфраструктуры БД. EXPLAIN и EXPLAIN ANALYZE, B-tree, GiST, SP-GiST, GIN, BRIN, частичные и составные индексы, типовые ошибки планировщика. Тонкая настройка памяти (shared_buffers, work_mem, maintenance_work_mem), I/O (effective_cache_size, random_page_cost), WAL, checkpoint и autovacuum. Документная модель в Postgres, операторы и индексы JSONB, declarative partitioning по range/list/hash, связь с оконными функциями и CTE. Хранимые функции и процедуры PL/pgSQL, row-level и statement triggers, event triggers, асинхронные события через NOTIFY и LISTEN без polling. Streaming replication, logical replication, read replicas и Hot Standby, failover, connection pooling через PgBouncer — transaction и session pooling. Официальный образ postgres, volumes для PGDATA, переменные окружения, docker-compose с healthcheck, типовые ошибки контейнеризации СУБД. Managed PostgreSQL (RDS, Cloud SQL, Yandex Managed), StatefulSet, PersistentVolume, секреты, операторы Crunchy/Zalando, anti-patterns stateful в K8s. Patroni и DCS, сравнение с Greenplum и CockroachDB, когда нужен sharding, Citus, выбор архитектуры под OLTP и аналитику. Краткое резюме раздела 8.11 — архитектура, оптимизация, эксплуатация в контейнерах и Kubernetes, HA, бэкапы и автоматизация.Бэкапы PostgreSQL и восстановление
Профилирование и мониторинг PostgreSQL
Автоматизация PostgreSQL — Ansible и Terraform
Продвинутая оптимизация PostgreSQL и индексы
Конфигурация PostgreSQL — postgresql.conf
JSONB, партиционирование и расширения SQL в PostgreSQL
PL/pgSQL, триггеры и NOTIFY/LISTEN в PostgreSQL
Репликация PostgreSQL, Hot Standby и PgBouncer
PostgreSQL в Docker
PostgreSQL в облаке и Kubernetes
HA-кластеры PostgreSQL и распределённые СУБД
Практикум PostgreSQL — итоги