awesome-everything EN
↑ Обратно к восхождению

Базы данных

Наблюдаемость, антипаттерны и производственный триаж

Суть USE + RED указывает какой рычаг тянуть; pg_stat_statements, pg_stat_activity и pg_stat_user_tables — это глаза. Postgres на 200M строк, 600 backends и 40 GB bloat имеет трёхнедельный порядок триажа — модель семи актов работает как аварийный протокол.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 16 min

Ты наследуешь Postgres на 200M строк, p95 1500 мс, 600 concurrent backends, 40 GB bloat, без pooler, одиночный узел. Три недели на стабилизацию. Модель семи актов — не просто история роста, это также протокол триажа. Работай от самого острого failure mode к самому структурному, а не наоборот.

USE + RED для Postgres

Метод USE (Utilization, Saturation, Errors) маппится на сигналы Postgres:

  • Utilization: число backends vs max_connections (pg_stat_activity), CPU из отношения pg_stat_bgwriter.checkpoints_req, hit rate буферного кеша (pg_buffercache).
  • Saturation: отставание autovacuum (pg_stat_user_tables.last_autovacuum), задержка репликации (pg_stat_replication.replay_lag), глубина очереди соединений (PgBouncer SHOW STATS).
  • Errors: число deadlock (pg_stat_database.deadlocks), ошибки checksum, ошибки репликации из серверных логов.

Метод RED (Rate, Errors, Duration) поверх pg_stat_statements:

  • Запросов/секунду на queryid.
  • Error rate из логов приложения (таймауты, deadlocks).
  • p95/p99 duration на queryid.

Вместе они говорят какой рычаг тянуть:

СигналВероятный акт
Высокое число backends, низкий CPUАкт 5 (проблема пулинга)
Много мёртвых tuples, стабильное число строкАкт 4 (проблема vacuum/bloat)
Конкретный queryid с растущей durationАкт 2 или 3 (проблема индекса или статистики)
Растущий p95 + горячие шарды одного tenantАкт 7 (проблема hot shard)

Диагностический инструментарий на каждый акт

  • Акт 1 (схема): нет хорошего инструментария — читай определение таблицы и трассируй миграции. \d+ tablename в psql.
  • Акт 2 (индексы): pg_indexes для проверки наличия; pg_stat_user_indexes (колонка idx_scan) для поиска неиспользуемых индексов — индекс с 0 сканов за неделю трафика это налог на write throughput.
  • Акт 3 (планирование): pg_stat_statements для топ-N запросов по total time; EXPLAIN (ANALYZE, BUFFERS) для реального плана; auto_explain.log_min_duration = '500ms' для триажа хвостовой латентности.
  • Акт 4 (bloat): pg_stat_user_tables.n_dead_tup vs n_live_tup; pg_table_size(relname) для реального диска; pgstattuple для точного процента bloat без полного скана.
  • Акт 5 (пулинг): pg_stat_activity для числа backends, распределения state, сессий idle in transaction; PgBouncer SHOW STATS для глубины очереди и среднего времени запроса.
  • Акт 6 (миграции): pg_locks join с pg_stat_activity для обнаружения lock contention и блокировщиков перед запуском ALTER.
  • Акт 7 (шардинг): метрики уровня приложения — хвостовая латентность на шард, дисперсия записей по шардам, частота cross-shard join-ов. Отсутствующий или неверно интерпретированный инструментарий — причина пропуска актов: «Мы не видим bloat» означает, что видимость autovacuum отключена, а не что bloat не существует.

Трёхнедельный порядок триажа

Наследуешь Postgres на 200M строк, p95 1500 мс, 600 concurrent backends, 40 GB bloat, без pooler:

Неделя 1 — остановить кровотечение:

  1. Убить long transactions: SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction' AND query_start < now() - interval '5 minutes'.
  2. Установить idle_in_transaction_session_timeout = 60s, чтобы проблема не повторилась за ночь.
  3. Развернуть PgBouncer в transaction-mode перед Postgres. Размер пула на основе active concurrent transactions, не числа workers (шаг 1 раскрывает реальный конкурентный уровень).
  4. Запустить pg_repack на раздутых таблицах онлайн — AccessExclusiveLock не требуется.

Неделя 2 — восстановить качество планов:

  1. ANALYZE на наиболее запрашиваемых таблицах.
  2. CREATE STATISTICS для топ коррелированных пар колонок из медленных запросов pg_stat_statements.
  3. Проверить топ-N в pg_stat_statements по total_time; удалить неиспользуемые индексы (idx_scan = 0); добавить очевидно отсутствующие.

Неделя 3 — оценка мощности:

  1. Измерить реальный single-node ceiling с пулом на месте. Построить кривую p95 vs QPS.
  2. Если одиночный узел не справляется с ожидаемой нагрузкой: оценить declarative partitioning (одиночный узел, меньше операций) перед шардингом.
  3. Только потом планировать Акт 7 — с shard key, co-location и онлайн-решардингом, отрепетированными в staging.

XID wraparound и autovacuum freeze

Каждая строка несёт 32-битный transaction ID (xmin). Когда кластер достигает 2^31 транзакций (~2.1 миллиарда), ID оборачиваются и строки могут казаться «из будущего». Postgres защищает от этого, замораживая старые строки (перезаписывая xmin специальным «замороженным» значением) до wraparound. autovacuum_freeze_max_age контролирует триггер.

На write-heavy кластере при 10K TPS это ~864M транзакций в день — freeze запускается каждые ~2.5 дня. Неверная конфигурация (очень высокий autovacuum_freeze_max_age) может привести к аварийным anti-wraparound vacuum, удерживающим эксклюзивную блокировку на таблице.

Мониторинг: SELECT datname, age(datfrozenxid) FROM pg_database; — алерт при 80% от autovacuum_freeze_max_age.

Каталог антипаттернов при пропуске акта

  • Пропусти Акт 1: год спустя ты тратишь три недели на переименование колонки, потому что в схеме нет суррогатного ключа и каждый FK должен меняться.
  • Пропусти Акт 2: каждый запрос дашборда — 30-секундный seq-scan; команда приложения добавляет кеши повсюду; кеш становится новым source of truth и рассинхронизируется.
  • Пропусти Акт 3: индексы есть, но планировщик игнорирует их половину времени; пишешь runbook, который говорит «если медленно, перезапусти Postgres», потому что это сбрасывает план кеш.
  • Пропусти Акт 4: OOM-killer убивает Postgres в пятницу; postmortem говорит «добавим мониторинг»; никогда не случается.
  • Пропусти Акт 5: каждый деплой триггерит connection storm; CI теперь растягивает pod rollouts на 10 минут «как воркэраунд».
  • Пропусти Акт 6: миграции запускаются только в квартальном окне обслуживания, потому что больше ничего безопасно; очередь ожидающих миграций вырастает до 40 записей.
  • Пропусти Акт 7: ты тратишь шесть месяцев объясняя руководству, что один tenant использует 40% мощности и единственное исправление — реархитектура.
Ключевые пороги наблюдаемости Postgres
Cache hit ratio — порог алерта
ниже 95–99%
n_dead_tup / n_live_tup — алерт на bloat
выше 20%
age(datfrozenxid) — алерт на wraparound
80% от autovacuum_freeze_max_age
PgBouncer SHOW STATS: avg_wait_time алерт
выше 5 мс sustained
pg_stat_statements total_time алерт
запросы > 1с потребляющие > 5% от total
Алерт на задержку репликации
выше 30 секунд на async replica
Почему это работает

Модель семи актов — одновременно growth framework (рычаги проектирования по порядку) и triage framework (применяй исправления в порядке обратном остроте сбоя). Во время аварии ты не винишь схему (Акт 1); ты убиваешь long transactions (Акт 4), затем развёртываешь pooler (Акт 5), затем оптимизируешь запросы (Акты 2–3). После восстановления стабильности делаешь postmortem и пишешь runbooks для каждого акта, чтобы предотвратить следующий инцидент. Порядок актов — не просто ограничение проектирования, это аварийный протокол.

Викторина

pg_stat_user_tables показывает n_dead_tup = 80M в таблице с n_live_tup = 100M. Правильный диагноз и первый рычаг?

Викторина

Какова роль backend_xmin в pg_stat_activity для диагностики bloat?

Викторина

Почему hot_standby_feedback = on переносит bloat с реплики на primary?

Вспомните перед уходом
  1. 01
    Сопоставь метод USE с сигналами Postgres, указывающими на каждый из семи актов.
  2. 02
    Ты наследуешь production Postgres на 200M строк, p95 1500 мс, 600 backends, 40 GB bloat, без pooler. Дай порядок триажа для первых трёх недель и обоснуй каждый шаг.
  3. 03
    Что такое XID wraparound, почему это порог корректности (а не производительности) и как его мониторить?
Итог

Фреймворк USE + RED маппится напрямую на семь актов: высокое число backends — Акт 5, высокое соотношение мёртвых tuples — Акт 4, растущая duration конкретного запроса — Акт 2 или 3, расходящаяся хвостовая латентность на шард — Акт 7. У каждого акта свой диагностический инструмент — pg_stat_user_tables для bloat, pg_stat_activity для connection storms, pg_stat_statements для медленных запросов, pg_locks для блокировщиков миграций, и метрики шардов уровня приложения для Акта 7. При наследовании деградированного Postgres правильный порядок триажа: Акт 4 первым (убить long transactions, остановить кровотечение), затем Акт 5 (развернуть pooler), затем Акты 3 и 2 (качество планов), затем структурные Акты 1 и 7. XID wraparound — порог корректности, он не замедляет запросы, он их портит — мониторь age(datfrozenxid) на базу и алерть при 80% от autovacuum_freeze_max_age. Модель семи актов — одновременно growth framework и аварийный протокол: порядок актов — это порядок триажа когда всё горит.

Связанные уроки
встречается в263
Продолжить восхождение ↑Трек баз данных: тест с выбором ответа
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources6
expand
  1. 01
  2. 02
  3. 03
  4. 04
  5. 05
  6. 06

Trademarks belong to their respective owners. Editorial reference only.