PostgreSQL LinuxWindowsmacOS

Почему PostgreSQL выбрал Seq Scan после ANALYZE и как проверить план

  • Обновлено
  • Чтение ≈ 4 минуты

Краткий ответ

После ANALYZE PostgreSQL может сменить Index Scan на Seq Scan, даже если индекс существует: планировщик пересчитывает стоимость на основе новых оценок распределения данных. Seq Scan сам по себе не ошибка — для небольших таблиц или невыборочных условий он может быть быстрее. Сначала сравните реальные затраты и оценку числа строк, затем решайте, нужна ли настройка статистики.

В этой статье
  1. Что означает ошибка
  2. Как различить причины
  3. Пошаговая диагностика и устранение
  4. Воспроизведение и проверка только в отдельной тестовой базе
  5. Как убедиться в исправлении и что делать дальше

#Что означает ошибка

После ANALYZE PostgreSQL может сменить Index Scan на Seq Scan, даже если индекс существует: планировщик пересчитывает стоимость на основе новых оценок распределения данных. Seq Scan сам по себе не ошибка — для небольших таблиц или невыборочных условий он может быть быстрее. Сначала сравните реальные затраты и оценку числа строк, затем решайте, нужна ли настройка статистики.

#Как различить причины

Возможная причинаКак узнатьЧто делать
Условия возвращают большую часть таблицыПри высокой доле подходящих строк чтение всей таблицы дешевле случайных обращений через индекс.EXPLAIN показывает Seq Scan, но время выполнения низкое, Rows Removed by Filter невелико.Не принуждать индекс, оставить быстрый план.
Оценки не совпадают с фактическими строкамиВыборка статистики не отражает редкие значения или зависимость между столбцами.В EXPLAIN ANALYZE строки rows=... и actual rows=... отличаются в разы.Исследовать pg_stats, точечно повысить статистику или создать extended statistics при необходимости.
Запрос или индекс не совпадаютИндекс по одному столбцу не используется для выражения над ним или другого порядка составных условий.EXPLAIN не показывает Index Cond, хотя индекс существует.Сверить условие, типы данных и структуру индекса; не пересоздавать индекс наугад.

#Пошаговая диагностика и устранение

  1. Запишите конкретный запрос и показатели до правкиОткройте интерфейс приложения или журнал запросов и найдите один SELECT, который замедлился после обновления статистики. Сохраните точный текст без паролей и личных данных, исходное время выполнения, объём таблицы и доступные планы. Если запрос изменяет данные, не запускайте его с EXPLAIN ANALYZE: последний реально выполняет команду.
  2. Подключитесь к нужной базе для чтения плановОткройте терминал и psql, либо pgAdmin → база → Tools → Query Tool. Проверьте SELECT current_database(), version();. Для каждой инструкции ниже подставляйте настоящие названия таблиц и индекса, взятые из базы. Если права запрещают доступ к pg_stats или таблице, не выдавайте себе дополнительные роли.
  3. Снимите план обычным EXPLAINВыполните EXPLAIN (VERBOSE, COSTS) SELECT ... для конкретного SELECT; он показывает предполагаемый план, не выполняя сам запрос. Найдите Seq Scan, Index Scan или Bitmap Heap Scan, Estimated Rows и стоимость. Если Seq Scan оказался быстрым на маленькой таблице, прекращайте исправление — нет оснований форсировать индекс.
  4. При наличии безопасного окна измерьте реальную выборкуТолько для SELECT, который не вызывает функций с побочными эффектами, согласуйте запуск EXPLAIN (ANALYZE, BUFFERS) SELECT ... на тестовой копии или при низкой нагрузке. Эта форма действительно выполняет запрос; сравните actual rows, execution time и чтение блоков. Если сервер загружен, используйте прежний план и не запускайте дорогое измерение на рабочем узле.
  5. Проверьте индексы и содержимое статистикиЗапросите SELECT indexname, indexdef FROM pg_indexes WHERE schemaname='public' AND tablename='orders'; с правильной схемой и таблицей. Затем посмотрите столбцы pg_stats для условий выборки, особенно null_frac, n_distinct, most_common_vals. Если название таблицы другое или статистика не видна роли, уточните схему; не пересоздавайте индексы без измерений.
  6. Уточните, когда действительно нужен ANALYZEЕсли после массовых изменений статистика устарела, согласуйте выполнение ANALYZE public.orders; в периоды допустимой нагрузки — команда читает данные и обновляет статистику, но не удаляет строки. Если ANALYZE уже выполнен и оценки выглядят реалистично, повторять его многократно бессмысленно. В случае крупной секционированной таблицы отдельно проверьте статистику родителя.
  7. Изолированно проверьте влияние способа доступаДля исследовательского сравнения на собственной тестовой копии откройте транзакцию, выполните SET LOCAL enable_seqscan=off; EXPLAIN SELECT ...; ROLLBACK;. Это лишь диагностический сигнал: принуждённый план может быть медленнее, а отключение последовательного чтения глобально опасно. После ROLLBACK параметр вернётся и в рабочем приложении ничего не изменится.
  8. Исправляйте только подтверждённую причинуЕсли оценки строк ошибаются, разработчик БД может точечно повысить статистику нужных столбцов или создать многоколоночную статистику по тестовому плану; это изменение схемы, требующее копии и сравнения планов. Если индекс не соответствует выражению, проектируйте подходящий индекс на тестовой базе. Если новый Seq Scan быстрее прежнего Index Scan, оставьте план без изменений.
  9. Команды для отдельных ситуацийВыберите только подходящий вариант. Это не одна последовательность: часть команд диагностирует проблему, а часть меняет настройки или данные.
    Выберите подходящий вариант — это не команды для последовательного выполнения
    Вариант 1Замените public и orders именами вашей схемы и таблицы, запрос только на чтение.
    SQLКоманда
    Замените public и orders именами вашей схемы и таблицы, запрос только на чтение.
    SELECT schemaname, tablename, indexname, indexdef FROM pg_indexes WHERE schemaname='public' AND tablename='orders';
    Вариант 2Наблюдение по столбцам.
    SQLКоманда
    Наблюдение по столбцам. Сами подробные значения распределения доступны в pg_stats при наличии прав на таблицу.
    SELECT schemaname, tablename, attname, null_frac, n_distinct FROM pg_stats WHERE schemaname='public' AND tablename='orders';
    Вариант 3Безопасный пример сравнения планов на системном каталоге, не выполняет SELECT.
    SQLКоманда
    Безопасный пример сравнения планов на системном каталоге, не выполняет SELECT. На вашей таблице замените запрос, результаты зависят от версии и статистики.
    BEGIN;
    SET LOCAL enable_seqscan = off;
    EXPLAIN SELECT * FROM pg_class WHERE oid = 1259;
    ROLLBACK;

#Воспроизведение и проверка только в отдельной тестовой базе

SQL
В тестовом подключении: сравните оценочный и фактический план безопасного SELECT к системному каталогу. Тип прохода и время могут быть разными, конкретный Index Scan не гарантируется.
EXPLAIN SELECT * FROM pg_class WHERE oid=1259;
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM pg_class WHERE oid=1259;

#Как убедиться в исправлении и что делать дальше

Сравните показатели выполнения на одинаковом наборе данных, прогретом кэше и сопоставимой нагрузке. Исправление нужно считать успешным, если реальное время и число прочитанных блоков уменьшились, а не просто появилось слово Index Scan. При ухудшении откатите экспериментальные настройки, сохраните оба плана и передайте их администратору базы.

Источники и обновления

Последнее обновление статьи: 2 ноября 2026. Версии программ и интерфейсы меняются — если шаги не совпадают с вашей версией, сверяйтесь с документацией производителя.