Какие production-риски есть у PostgreSQL: блокировки, bloat, replication lag, backup/restore, schema changes или плохие запросы?
Ключевые риски: долгие блокировки (DDL + длинные транзакции), table/index bloat без своевременного vacuum, репликационный lag и брошенные replication slots, отсутствие проверенных backup, опасные schema changes без CONCURRENTLY/NOT VALID.
Обзор production-рисков PostgreSQL
PostgreSQL — надёжная СУБД, но в production она генерирует специфические классы аварий, каждый из которых требует отдельной системы предотвращения и реагирования.
Блокировки (Locks)
PostgreSQL использует многоуровневую систему блокировок. Самые опасные — ACCESS EXCLUSIVE (DDL-операции) и длинные транзакции, которые держат строчные блокировки.
-- Найти ожидающие и блокирующие запросы
SELECT
blocked.pid,
blocked.query AS blocked_query,
blocking.pid AS blocking_pid,
blocking.query AS blocking_query,
blocked.wait_event_type,
blocked.wait_event
FROM pg_stat_activity AS blocked
JOIN pg_stat_activity AS blocking
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE blocked.wait_event_type = 'Lock';
-- Превентивные меры
ALTER SYSTEM SET lock_timeout = '5s';
ALTER SYSTEM SET deadlock_timeout = '1s';
ALTER SYSTEM SET idle_in_transaction_session_timeout = '30s';
SELECT pg_reload_conf();
Table Bloat и Index Bloat
Dead tuples накапливаются быстрее, чем autovacuum успевает их убирать. Это особенно критично для таблиц с высоким UPDATE-трафиком (например, очереди задач, счётчики).
-- Оценка bloat по таблицам
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname || '.' || tablename)) AS total_size,
n_dead_tup,
n_live_tup,
ROUND(100.0 * n_dead_tup / NULLIF(n_live_tup + n_dead_tup, 0), 1) AS dead_pct
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;
Для устранения bloat без блокировок используйте pg_repack: он переписывает таблицу в фоне с минимальным lock в конце.
Replication Lag
Отставание реплики критично при failover (потеря данных) и при использовании реплики как read-only (грязные чтения).
-- На primary: lag всех реплик в байтах и секундах
SELECT
application_name,
state,
pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) AS replay_lag_bytes,
replay_lag
FROM pg_stat_replication;
-- На реплике
SELECT now() - pg_last_xact_replay_timestamp() AS replication_delay;
Причины лага: большие транзакции, медленный I/O на реплике, сеть, replication slots без consumer. Репликационные слоты без активного подписчика блокируют очистку WAL — это приводит к переполнению диска.
Backup / Restore
Две принципиально разные стратегии: pg_dump (логический, не требует downtime, но медленный restore) и base backup + WAL архив (PITR, позволяет восстановить на любой момент времени).
# pg_dump — снапшот без блокировки таблиц
pg_dump -Fc -d mydb -f mydb_$(date +%Y%m%d).dump
# pg_basebackup — физический снапшот
pg_basebackup -h localhost -U replicator -D /backup/base -Ft -z -P
# Проверка: restore ОБЯЗАТЕЛЕН, иначе backup не считается валидным
pg_restore -d mydb_test mydb_$(date +%Y%m%d).dump
Schema Changes на живой таблице
ALTER TABLE ADD COLUMN без DEFAULT безопасна с PG 11+. Но добавление DEFAULT или NOT NULL, создание индексов без CONCURRENTLY, добавление FK с VALIDATE — всё это берёт долгий lock.
-- Безопасное добавление индекса
CREATE INDEX CONCURRENTLY idx_orders_user_id ON orders(user_id);
-- Безопасное добавление FK без долгой блокировки
ALTER TABLE orders ADD CONSTRAINT fk_user
FOREIGN KEY (user_id) REFERENCES users(id)
NOT VALID; -- не проверяет существующие строки
ALTER TABLE orders VALIDATE CONSTRAINT fk_user; -- отдельно, с ShareLock
Плохие запросы
-- Включить pg_stat_statements (один раз)
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-- Топ-10 запросов по суммарному времени
SELECT
LEFT(query, 100) AS query_snippet,
calls,
ROUND(total_exec_time::numeric, 2) AS total_ms,
ROUND(mean_exec_time::numeric, 2) AS avg_ms,
rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
Подводные камни
- Replication slot без consumer — блокирует удаление WAL-сегментов и может заполнить диск на 100%, что убивает весь кластер. Мониторьте
pg_replication_slotsи удаляйте брошенные слоты. - VACUUM FULL в пиковые часы — берёт ACCESS EXCLUSIVE lock, блокирует всех пользователей. Планируйте в окна обслуживания или используйте pg_repack.
- Отсутствие тестового restore — pg_dump может завершиться без ошибок, но содержать повреждённые данные. Автоматизируйте restore в тестовую среду ежесуточно.
- Долгие транзакции во время DDL — ALTER TABLE ждёт всех активных транзакций и потом блокирует все новые. В busy системе это может держать lock минутами.
- Seq Scan на больших таблицах без индекса убивает I/O. Используйте
EXPLAIN (ANALYZE, BUFFERS), а не просто EXPLAIN. - Connection storm при restart — все клиенты одновременно пытаются переподключиться. Используйте PgBouncer с retry logic на стороне клиента.
- Checkpoint pressure — если
bgwriterне справляется, WAL checkpoint случается слишком часто и создаёт I/O пики. Настройтеcheckpoint_completion_target = 0.9иmax_wal_size.
What hurts your answer
- Говорить только о запуске PostgreSQL, но не об эксплуатации
- Не упоминать observability, обновления, безопасность и rollback
- Описывать риски абстрактно, без способов их снижать
What they're listening for
- Видит production-риски PostgreSQL
- Говорит про monitoring, rollout, rollback и безопасность
- Умеет ранжировать риски по вероятности и влиянию