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

Базы данных

pg_statistic, ANALYZE и производственная наблюдаемость

Суть Оценки строк планировщика берутся из pg_statistic: MCV-списки, гистограммы, корреляция, n_distinct. ANALYZE поддерживает их свежесть. auto_explain и pg_stat_statements вместе говорят, какие запросы медленные и почему.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на middle-высоте — в небе
◷ 16 min

Запрос, выполнявшийся за 5 мс вчера, сегодня занимает 4 секунды. В приложении ничего не изменилось. План другой. Причина почти всегда одна: статистика устарела и планировщик сделал другой выбор. Знание того, как Postgres строит и использует статистику, — вот как предотвратить следующий инцидент.

Внутри pg_statistic

При запуске ANALYZE orders Postgres выборочно сканирует таблицу (по умолчанию 300 × default_statistics_target = 30,000 строк при целевом значении 100) и вычисляет статистику на уровне колонок, хранящуюся в pg_statistic (читается через pg_stats):

СтатистикаЧто хранитИспользуется для
most_common_vals (MCV)Top-N значений + их частотыТочная селективность для частых значений
histogram_boundsРавночастотная гистограмма не-MCV значенийСелективность для диапазонных и равенственных условий на редких значениях
n_distinctКоличество уникальных значений (отрицательное = доля строк)Оценки кардинальности GROUP BY
correlationКорреляция физического и логического порядка (−1 до 1)Оценка стоимости случайного vs последовательного I/O при index scan

Как планировщик использует это для WHERE x = 42:

  1. Есть ли 42 в MCV-списке? Используем его точную частоту напрямую.
  2. Не MCV? Находим бакет гистограммы, содержащий 42, предполагаем равномерное распределение внутри бакета.
  3. Комбинируем с reltuples (счётчик строк в pg_class) для получения оценки количества строк.

Для диапазонных предикатов (x > 100) гистограмма интегрируется по диапазону. Для многоколоночных предикатов планировщик по умолчанию предполагает независимость и перемножает селективности — что неверно для коррелированных колонок (рассматривается в уроке 05).

Чтайте каталог напрямую:

SELECT tablename, attname, n_distinct, most_common_vals, most_common_freqs,
       histogram_bounds, correlation
FROM pg_stats
WHERE tablename = 'orders';

ANALYZE: когда запускать

Autovacuum планирует ANALYZE для каждой таблицы на основе:

autovacuum_analyze_threshold (умолчание 50 строк) +
autovacuum_analyze_scale_factor × reltuples (умолчание 0.1 = 10%)

На таблице из 100M строк autovacuum анализирует только после 10M изменений строк — слишком редко для таблиц с косыми или быстро меняющимися распределениями данных. Операционные исправления:

-- Снизьте scale factor для горячих таблиц:
ALTER TABLE orders SET (autovacuum_analyze_scale_factor = 0.02);

-- Поднимите целевой показатель статистики для косых колонок:
ALTER TABLE orders ALTER COLUMN status SET STATISTICS 1000;
ANALYZE orders;

Ручной ANALYZE выполняется за секунды даже на таблицах из 100M строк (он делает выборку, а не полное сканирование). Всегда запускайте его:

  • После массовых вставок или больших UPDATE, сдвигающих распределения
  • После изменений схемы, добавляющих колонки
  • В post-deploy хуках перед возобновлением трафика

Опции EXPLAIN для диагностики

Помимо базового плана, несколько опций EXPLAIN необходимы:

  • FORMAT JSON | XML | YAML | TEXT — JSON для инструментов (explain.depesz.com, explain.dalibo.com, pganalyze)
  • VERBOSE — добавляет списки выходных колонок для каждого узла
  • SETTINGS (PG 12+) — печатает не-дефолтные GUC планировщика; диагностирует дрейф окружения между staging и production
  • WAL (PG 13+) — показывает байты WAL, сгенерированные оператором
  • GENERIC_PLAN (PG 16+) — планирует параметризованный запрос без примерных значений; необходим для диагностики подготовленных операторов (рассматривается в уроке 06)
  • SERIALIZE (PG 17+) — включает стоимость сериализации строк для клиента; сокращает разрыв между полным временем EXPLAIN ANALYZE и наблюдаемой клиентом задержкой

auto_explain и pg_stat_statements

Два расширения, которые должны быть на каждом production Postgres:

pg_stat_statements записывает каждый выполненный запрос (нормализованный по параметрам), отслеживая calls, total_exec_time, mean_exec_time, rows и счётчики буферов. Запрос: SELECT query, calls, total_exec_time, mean_exec_time FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 20. Эти top-20 по total_exec_time — ваши цели оптимизации — запрос за 2 мс, вызванный 10M раз, важнее запроса за 500 мс, вызванного дважды.

auto_explain автоматически логирует EXPLAIN ANALYZE для любого запроса, превышающего порог длительности:

-- Дополнения к postgresql.conf:
shared_preload_libraries = 'auto_explain'
auto_explain.log_min_duration = '500ms'
auto_explain.log_analyze = true
auto_explain.log_buffers = true
auto_explain.log_format = 'json'
auto_explain.sample_rate = 0.01   -- 1% медленных запросов для ограничения объёма логов

Медленные запросы попадают в логи Postgres с полными планами ANALYZE + BUFFERS, без необходимости воспроизводить их на staging. Вместе они отвечают на вопрос «какие запросы медленные и почему» без инструментирования приложения.

Проследи
1/5

Запрос, выполнявшийся за 5 мс вчера, сегодня занимает 4 секунды. В приложении ничего не изменилось. Диагностируйте.

1
Step 1 of 5
Шаг 1: подтвердить регрессию.
2
Locked
Шаг 2: проверить отличия во время планирования.
3
Locked
Шаг 3: проверить отличия во время исполнения.
4
Locked
Шаг 4: выявить ловушку подготовленных операторов.
5
Locked
Шаг 5: стабилизировать.
Расставь шаги по порядку

Упорядочьте диагностические проверки EXPLAIN ANALYZE от наибольшего к наименьшему сигналу:

  1. 1 Rows estimated vs rows actual на каждом узле — большой разрыв = устаревшая статистика
  2. 2 Узел плана, занимающий наибольшее реальное время — доминирующая стоимость запроса
  3. 3 Счётчик loops на внутренней стороне Nested Loop — обнажает недооценку внешних строк
  4. 4 Sort Method (in-memory quicksort vs external merge) — помещается ли в work_mem
  5. 5 BUFFERS shared hit vs read — состояние кеша и картина I/O
  6. 6 Index Cond vs Filter — индекс сужает или только пост-фильтрует
  7. 7 Planning Time vs Execution Time — доминирование планирования редко, но реально
Викторина

На таблице из 100M строк с настройками autovacuum по умолчанию, после скольких изменений строк autovacuum запустит ANALYZE?

Викторина

Какой запрос правильно определяет цели медленных запросов для оптимизации с помощью pg_stat_statements?

Вспомните перед уходом
  1. 01
    Опишите, как планировщик Postgres использует pg_statistic для оценки селективности WHERE x = 42.
  2. 02
    Какая операционная дисциплина поддерживает точность статистики в быстро меняющейся production базе данных?
  3. 03
    Объясните, что делает auto_explain, как его настроить для production и какова его стоимость.
Итог

Оценки строк планировщика вычисляются из pg_statistic: для каждой колонки ANALYZE хранит список наиболее распространённых значений (MCV) с точными частотами, равночастотную гистограмму для остальных, n_distinct для кардинальности GROUP BY и correlation для оценки стоимости индексного сканирования. ANALYZE выборочно сканирует таблицу (30,000 строк по умолчанию) и выполняется за секунды даже на больших таблицах. Autovacuum запускает его после 10% изменений строк на 100M-строчной таблице — слишком редко для горячих данных; настройте autovacuum_analyze_scale_factor = 0.02 на критических таблицах и запускайте ANALYZE вручную в post-deploy хуках. Для наблюдаемости в production pg_stat_statements ранжирует запросы по total_exec_time для выявления целей оптимизации; auto_explain автоматически захватывает планы медленных запросов без ручного воспроизведения.

Практика

Сделай это, чтобы превратить узнавание в навык.

Связанные уроки
встречается в258
Продолжить восхождение ↑Расширенная статистика: исправление ошибок оценки для коррелированных колонок
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.