2. Эксплуатация и стабильность: VACUUM, WAL, Backup & Restore
VACUUM и Autovacuum (мёртвые строки, bloat, wraparound), WAL и checkpoints (fsync, влияние на I/O), резервное копирование и восстановление (pg_dump, pg_basebackup, PITR, инструменты pgBackRest и wal-g). В разделе — примеры кода и SQL с комментариями и best practices для production.
4. VACUUM и Autovacuum
Почему без VACUUM «всё умирает»
При MVCC обновления и удаления не затирают старые версии строк сразу — они остаются в таблице как dead tuples (мёртвые кортежи). Пока их не обработает VACUUM, они занимают место, раздувают индексы и замедляют сканы. Без VACUUM таблицы и индексы растут (bloat), запросы тормозят; при исчерпании transaction ID возможен wraparound — база переходит в режим только vacuum до завершения анти-wraparound vacuum.
Dead tuples и мониторинг
-- Количество живых и мёртвых строк по таблицам (ключевая метрика для autovacuum)
SELECT
schemaname,
relname,
n_live_tup,
n_dead_tup,
round(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 2) AS dead_pct,
last_vacuum,
last_autovacuum,
last_analyze,
last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;
-- Расширенная статистика по всем таблицам (включая системные)
SELECT relname, n_tup_ins, n_tup_upd, n_tup_del, n_live_tup, n_dead_tup
FROM pg_stat_all_tables
WHERE schemaname = 'public'
ORDER BY n_dead_tup DESC;
Wraparound
Transaction ID в PostgreSQL ограничен 32 битами; после ~2 млрд транзакций счётчик «заворачивается». Чтобы старые строки не казались «будущими», нужен anti-wraparound VACUUM — он помечает старые версии как замороженные (frozen). Обычно за это отвечает autovacuum; при приближении к порогу wraparound autovacuum агрессивно вакуумит таблицы. Мониторить возраст самой старой транзакции:
-- Возраст самой старой незамороженной транзакции (предупреждение при приближении к 200M)
SELECT datname, age(datfrozenxid) AS frozen_age
FROM pg_database
WHERE datname = current_database();
Параметры Autovacuum
# postgresql.conf — типичные настройки для production
autovacuum = on
autovacuum_naptime = 1min
autovacuum_vacuum_threshold = 50
autovacuum_analyze_threshold = 50
autovacuum_vacuum_scale_factor = 0.1 # Запуск vacuum при 10% мёртвых строк
autovacuum_analyze_scale_factor = 0.05
autovacuum_vacuum_cost_delay = 2ms
autovacuum_vacuum_cost_limit = 2000
Порог запуска vacuum: vacuum_threshold + scale_factor * n_live_tup. Для больших таблиц scale_factor 0.1 может давать слишком большой порог — тогда уменьшают scale_factor или задают per-table настройки:
-- Для очень большой таблицы: снизить порог (vacuum чаще)
ALTER TABLE big_events SET (
autovacuum_vacuum_scale_factor = 0.02,
autovacuum_analyze_scale_factor = 0.01
);
Симуляция bloat и ручной VACUUM
-- Создать таблицу и нагенерировать мёртвые строки (UPDATE без изменения данных тоже создаёт версии)
CREATE TABLE t_bloat_demo (id int PRIMARY KEY, val text);
INSERT INTO t_bloat_demo SELECT i, 'x' FROM generate_series(1, 10000) i;
-- Много обновлений — много dead tuples
UPDATE t_bloat_demo SET val = val WHERE id % 2 = 0;
UPDATE t_bloat_demo SET val = val WHERE id % 2 = 0;
-- Проверить n_dead_tup
SELECT n_live_tup, n_dead_tup FROM pg_stat_user_tables WHERE relname = 't_bloat_demo';
-- Ручной VACUUM (не блокирует чтение/запись, в отличие от VACUUM FULL)
VACUUM (ANALYZE) t_bloat_demo;
-- VACUUM FULL перезаписывает таблицу, требует эксклюзивной блокировки — только при сильном bloat и в окно обслуживания
-- VACUUM FULL t_bloat_demo;
Best practices: VACUUM и Autovacuum
- Не отключать autovacuum в production; при необходимости тюнить пороги и cost, а не отключать.
- Мониторить
n_dead_tup,last_autovacuum, возрастdatfrozenxid; алертить на высокий dead_pct и приближение к wraparound. - VACUUM FULL — только при доказанном сильном bloat и в окно обслуживания; в остальных случаях достаточно обычного VACUUM и при необходимости реиндексации.
5. WAL и Checkpoints
Зачем нужен WAL
WAL обеспечивает долговечность: изменения сначала записываются в WAL и сбрасываются на диск (fsync); только затем страницы данных могут быть записаны позже. При сбое восстановление идёт по WAL от последнего checkpoint. Рост WAL ограничивается checkpoint'ами: при checkpoint грязные буферы сбрасываются на диск, и старые сегменты WAL можно переиспользовать или удалить (при архивировании — копировать в архив).
fsync и synchronous_commit
- fsync — сбрасывать ли изменения файлов на диск (отключение опасно при сбое питания).
- synchronous_commit = on — ждать подтверждения записи WAL на диск перед ответом клиенту. synchronous_commit = off или local — быстрее, но при сбое возможна потеря последних транзакций. В production обычно оставляют on; для некритичных нагрузок допустимо off с пониманием риска.
SHOW synchronous_commit;
-- Для репликации: synchronous_commit = 'on' и в primary настроен synchronous_standby_names при необходимости
Влияние checkpoints на I/O
При checkpoint все грязные буферы до текущей точки WAL записываются на диск — возможен всплеск I/O. Параметры max_wal_size и checkpoint_timeout задают, как часто это происходит: больший max_wal_size реже принудительный checkpoint, но больше WAL на диске.
-- Статистика checkpoint'ов (частые checkpoints_req — признак нехватки WAL или нагрузки)
SELECT checkpoints_timed, checkpoints_req, buffers_checkpoint, buffers_backend
FROM pg_stat_bgwriter;
Мониторинг роста WAL
-- Текущий сегмент WAL и позиция
SELECT pg_current_wal_lsn(), pg_walfile_name(pg_current_wal_lsn());
-- Размер директории WAL (на сервере; путь из data_directory + pg_wal)
-- ls -la $PGDATA/pg_wal
# На сервере: размер каталога WAL
du -sh /var/lib/postgresql/data/pg_wal
Ситуация «диск внезапно закончился»
Частые причины: взрывной рост WAL (долгие транзакции, репликация не успевает забирать WAL, архив не успевает), накопление сегментов при сбое архивирования. Действия:
- Освободить место (удалить старые логи, временные файлы) или расширить диск.
- Проверить реплику и архив: не застряла ли доставка WAL; при необходимости временно увеличить wal_keep_size или исправить архив.
- Убедиться, что checkpoint может завершиться: при нехватке места для WAL сервер может не давать писать новые сегменты.
В production — мониторинг свободного места на диске с данными и WAL, алерты до заполнения.
6. Backup & Restore
pg_dump и pg_dumpall (логический бэкап)
Логический бэкап — дамп SQL или custom-формат; восстанавливается через pg_restore или psql. Удобен для переноса между версиями и выборочного восстановления объектов.
# Дамп одной БД в custom-формат (сжатие, параллельный restore)
pg_dump -Fc -f /backup/mydb_$(date +%Y%m%d).dump mydb
# Дамп одной БД в SQL (для мелких БД или переноса)
pg_dump -f /backup/mydb.sql mydb
# Дамп всех БД + глобальные объекты (роли, табличные пространства)
pg_dumpall -f /backup/all_$(date +%Y%m%d).sql
# Восстановление из custom-формата (создать БД заранее при необходимости)
pg_restore -d mydb -j 4 /backup/mydb_20250101.dump
# Восстановление из SQL
psql -d mydb -f /backup/mydb.sql
pg_basebackup (физический полный бэкап)
Физический бэкап — копия файлов кластера (data directory). pg_basebackup создаёт согласованный снимок с текущей позицией WAL; для PITR нужен ещё архив WAL.
# Полный бэкап в каталог (требуется настроенный доступ репликации в pg_hba.conf)
pg_basebackup -D /backup/base_$(date +%Y%m%d) -Ft -z -P -U replicator
# -D каталог, -Ft tar + сжатие, -z gzip, -P прогресс
Восстановление из base backup: остановить сервер, подставить файлы из бэкапа, при необходимости задать recovery_target_time и настроить restore_command для подтягивания WAL из архива, затем запустить — сервер дотянет WAL и выйдет из recovery.
Full vs logical backup
| Тип | Инструмент | Плюсы | Минусы |
|---|---|---|---|
| Logical | pg_dump / pg_dumpall | Выборочное восстановление, перенос между версиями | Медленнее на больших объёмах, не «побайтовый» снимок |
| Physical | pg_basebackup + WAL архив | Быстрый полный restore, основа для PITR | Восстановление только той же мажорной версии, нужен архив WAL для PITR |
В production обычно комбинируют: регулярный physical (pg_basebackup или pgBackRest/wal-g) + архив WAL для PITR; при необходимости логический дамп для переноса или выборочного восстановления.
Point-in-Time Recovery (PITR)
PITR: восстановление до произвольного момента в пределах архива WAL. Нужны: полный бэкап (base backup) + непрерывный архив WAL. В каталоге данных создаётся recovery.spec (или в PostgreSQL 12+ postgresql.auto.conf и standby.signal / настройки restore) с recovery_target_time и restore_command.
# recovery.spec (или в postgresql.conf при старых версиях)
recovery_target_time = '2025-01-15 14:30:00'
restore_command = 'cp /archive/wal/%f %p'
После восстановления до нужной точки сервер выходит из recovery и принимает подключения.
Инструменты: pgBackRest, wal-g
- pgBackRest — полные и инкрементальные бэкапы, сжатие, параллелизм, дельта restore, архив WAL. Конфиг в отдельном файле; интеграция с S3, GCS, Azure.
- wal-g — архив WAL и бэкапы в облачное хранилище (S3 и др.); быстрый инкрементальный бэкап и restore. Удобен в облачных окружениях.
Оба подходят для production; выбор по экосистеме (облако, уже используемые инструменты) и требованиям к RPO/RTO.
Практика: восстановить БД из бэкапа
- Логический: создать пустую БД, выполнить
pg_restore -d mydb backup.dumpилиpsql -d mydb -f backup.sql. - Физический + PITR: остановить сервер; подставить данные из base backup; задать restore_command и при необходимости recovery_target_time; запустить сервер; дождаться выхода из recovery.
- Проверить целостность и актуальность данных после восстановления.
Best practices для production
| Область | Рекомендация |
|---|---|
| VACUUM | Мониторить n_dead_tup и last_autovacuum; тюнить autovacuum по таблицам при необходимости; не отключать. |
| WAL | Достаточный объём диска под pg_wal и архив; мониторить место; алертить на частые checkpoints_req. |
| Backup | Регулярные полные бэкапы + архив WAL; хранить вне сервера; периодически проверять restore. |
| PITR | Тестировать восстановление до точки во времени в staging; документировать restore_command и процедуру. |
Паттерны и антипаттерны
| Паттерн | Описание |
|---|---|
| Автоматические бэкапы + проверка restore | Регулярный прогон восстановления в тестовом окружении. |
| Мониторинг dead tuples и WAL | Раннее выявление bloat и нехватки места. |
| Архив WAL вне сервера | Возможность PITR и восстановления при потере диска. |
| Антипаттерн | Почему плохо | Что делать |
|---|---|---|
| Отключить autovacuum | Рост bloat и риск wraparound. | Настраивать пороги и cost, не отключать. |
| Бэкапы только на тот же диск | При отказе диска теряются и данные, и бэкап. | Копировать в другое хранилище (другой диск, объектное хранилище). |
| Никогда не проверять restore | В момент аварии процедура может не сработать. | Периодически восстанавливать из бэкапа в тестовый кластер. |
Дополнительные материалы
- PostgreSQL — Routine Vacuuming
- PostgreSQL — Write-Ahead Log
- PostgreSQL — Backup and Restore
- pgBackRest
- wal-g
- 1. База PostgreSQL — MVCC, WAL, конфигурация