Postgres в проде: соединения и миграции
Почему соединения — дефицитный ресурс, зачем нужен пул и как не положить прод миграцией.
Соединение — дорогая вещь
Каждое подключение к Postgres — отдельный процесс на сервере базы с собственной памятью. Поэтому max_connections — не «поставим 10000 и забудем»: сотни простаивающих соединений съедают память и планировщик.
Классическая ловушка масштабирования: приложение держит пул на 20 соединений — прекрасно. Потом инстансов приложения становится 10, и база получает 200 постоянных подключений. Формула, которую стоит помнить: соединений у базы = размер пула × число инстансов (плюс воркеры, кроны и аналитики с psql).
Пулы
Первый уровень — пул в самом приложении (он есть в каждом драйвере): соединения переиспользуются, а не открываются на каждый запрос.
Когда инстансов много, добавляют PgBouncer — внешний пул между приложениями и базой. Сотни клиентских подключений он обслуживает десятком реальных, в режиме transaction pooling соединение выдаётся только на время транзакции.
Таймауты — обязательны
Запрос без таймаута может держать соединение вечно, и однажды весь пул окажется занят зависшими запросами:
-- на уровне роли приложения
ALTER ROLE app SET statement_timeout = '5s';
ALTER ROLE app SET idle_in_transaction_session_timeout = '30s';
Второй параметр закрывает отдельную беду: транзакция открыта, приложение ушло думать — а база всё это время держит блокировки.
Миграции, которые кладут прод
DDL в Postgres берёт блокировки. Три частых случая:
| Миграция | Что происходит |
|---|---|
ADD COLUMN ... DEFAULT ... |
В современных версиях — мгновенно; в старых переписывала всю таблицу |
CREATE INDEX |
Блокирует запись на всё время построения |
ALTER TABLE ... SET NOT NULL |
Полное сканирование под блокировкой |
Правила выживания:
CREATE INDEX CONCURRENTLY idx_orders_user ON orders (user_id);
- индексы на больших таблицах — только
CONCURRENTLY; lock_timeoutперед миграцией: лучше упасть и повторить, чем стоять в очереди за блокировкой, собирая за собой все запросы;- большие изменения — в несколько шагов, каждый из которых быстрый и обратимый.