PostgreSQL

Troubleshooting: диагностика production проблем

3 часа ночи, production не отвечает. CPU 100%, приложение зависло. Как за 5 минут найти причину и восстановить работу?

  • GitLab публично разбирал инцидент с deadlock при деплое миграции - lock contention на 30 минут из-за одного ALTER TABLE
  • Discord потерял $140K дохода за время инцидента с connection exhaustion до внедрения PgBouncer
  • Notion расследовал CPU spike от 1% до 95% через pg_stat_statements - оказался один запрос с неправильным индексом

CPU spike: диагностика и устранение

CPU spike в PostgreSQL - это резкий рост потребления процессора, часто до 100%. Причины: внезапно деградировавший план запроса, запрос без индекса на большой таблице, autovacuum на перегретой таблице, или шторм параллельных аналитических запросов.

Типичный сценарий: статистика устарела после массовой загрузки данных, планировщик переоценил селективность индекса и начал делать Seq Scan на таблице в 100 млн строк. Решение: ANALYZE таблицы для обновления статистики, затем EXPLAIN ANALYZE для проверки нового плана.

SET enable_seqscan = off использовать только для диагностики в сессии, никогда на уровне конфигурации сервера. Это ломает плановщик для запросов, где Seq Scan действительно оптимален.

PostgreSQL внезапно загрузил CPU до 95%. Первый шаг диагностики?

Disk full: PostgreSQL кончилось место

Disk full в PostgreSQL - критичная ситуация: база переходит в read-only, WAL архивирование останавливается, replication lag растёт. Место заканчивается по трём причинам: bloat (мёртвые tuple без vacuum), WAL накопление из-за незакрытого replication slot, или рост pg_wal при интенсивной записи.

Наиболее коварная причина: неактивный replication slot. PostgreSQL не удаляет WAL-файлы, пока слот не подтвердит их применение. Если subscriber отключился (но слот остался), WAL накапливается и съедает весь диск. За несколько часов pg_wal может вырасти с 1 GB до 100+ GB.

Превентивная мера: alerting на retained_wal > 10 GB по pg_replication_slots. Также полезен параметр max_slot_wal_keep_size (PG 13+) - ограничивает объём WAL, удерживаемого слотом.

PostgreSQL pg_wal вырос до 80 GB за ночь. Наиболее вероятная причина?

Replication lag: replica отстаёт

Replication lag - это отставание standby от primary по WAL. При асинхронной репликации допустимый лаг зависит от RPO. При лаге > нескольких секунд приложение рискует читать устаревшие данные со standby. При лаге > 30 секунд автоматический failover может привести к потере данных.

Причины replication lag: 1) Долгий запрос на standby в hot_standby блокирует применение WAL-конфликтов - настройка max_standby_streaming_delay позволяет стандбаю отменить конфликтующий запрос; 2) Сетевая пропускная способность между primary и standby; 3) Тяжёлые запросы на standby нагружают IO, замедляя применение WAL.

hot_standby_feedback=on снижает lag, но увеличивает bloat на primary: primary не может vacuum строки, видимые транзакцией на standby. Компромисс: включать только если standby активно используется для чтения и lag критичен.

Replication lag на standby вырос до 5 минут. Как диагностировать причину?

Connection exhaustion: too many connections

Ошибка "FATAL: sorry, too many clients already" означает достижение max_connections. Каждое соединение в PostgreSQL - это отдельный процесс (~5-10 MB RSS). При 1000 соединениях сервер тратит 5-10 GB RAM только на процессы, независимо от нагрузки.

Решение: PgBouncer в transaction pooling mode. При transaction pooling клиент занимает соединение только на время транзакции. 10,000 клиентов → 50 реальных соединений к PostgreSQL. Discord обслуживает 1M+ одновременных пользователей через PgBouncer с pool_size=25 на каждую базу.

Transaction pooling несовместим с: SET/RESET session parameters, LISTEN/NOTIFY, prepared statements (если не включён pool_mode=session для конкретных приложений), advisory locks. Для приложений, использующих эти фичи, применять session mode или отдельный pool.

10,000 клиентов подключаются к PostgreSQL с max_connections=200. Что произойдёт?

Lock contention: deadlocks и долгие блокировки

Lock contention - ситуация, когда транзакции блокируют друг друга, ожидая освобождения строк или таблиц. Признаки: транзакции зависают на lock wait, дерево блокировок растёт, latency API растёт нелинейно. Deadlock - когда два процесса блокируют друг друга циклически: PostgreSQL автоматически детектирует и отменяет одну из транзакций.

Типичные паттерны lock contention: 1) UPDATE без WHERE на большой таблице блокирует все остальные UPDATE; 2) Долгая транзакция держит row lock, накапливая очередь ожидающих; 3) ALTER TABLE берёт AccessExclusiveLock и застревает за долгой транзакцией; 4) Deadlock при обновлении двух строк в разном порядке.

Для мониторинга lock contention в реальном времени: pg_stat_activity с фильтром wait_event_type = 'Lock' + alert при count > 5. Grafana dashboard с lock wait time помогает детектировать паттерны до появления инцидентов.

Перезапуск PostgreSQL при production-инциденте - быстрое решение проблемы

Перезапуск уничтожает shared_buffers (кеш прогревается часами), прерывает все соединения и не устраняет первопричину. Любую проблему нужно диагностировать и решать точечно.

После перезапуска: cold start без кеша даёт в 3-10 раз больше disk IO, все активные транзакции откатываются, приложения получают connection reset. Если проблема в плохом запросе или deadlock - он вернётся сразу. Правильный подход: pg_cancel_backend для конкретного запроса, pg_terminate_backend для зависшей транзакции.

Что такое deadlock в PostgreSQL и как он разрешается?

Troubleshooting PostgreSQL

  • CPU spike: pg_stat_activity → найти долгий запрос → EXPLAIN ANALYZE → ANALYZE для обновления статистики
  • Disk full: проверить replication slots (retained WAL) → pg_drop_replication_slot → VACUUM на bloated таблицах
  • Replication lag: pg_stat_replication на primary + pg_stat_database_conflicts на standby
  • Connection exhaustion: PgBouncer transaction pooling - 10,000 клиентов → 25 реальных соединений
  • Lock contention: pg_blocking_pids для дерева блокировок → pg_cancel_backend корня → lock_timeout превентивно

Связанные темы

Troubleshooting опирается на знание всей системы

  • Мониторинг — Проактивный мониторинг pg_stat_activity и pg_stat_statements предотвращает инциденты
  • Autovacuum — Настроенный autovacuum предотвращает bloat и disk full от dead tuples
  • Connection Pooling — PgBouncer - решение connection exhaustion и снижение overhead на process creation

Вопросы для размышления

  • Как pg_stat_activity помогает отличить проблему с CPU от проблемы с lock contention?
  • Почему незакрытый replication slot опаснее просто медленного роста таблиц для диска?
  • В каких случаях перезапуск PostgreSQL оправдан, а в каких - только ухудшит ситуацию?

Связанные уроки

  • db-11-query-optimization
Troubleshooting: диагностика production проблем

0

1

Войти