Одинаковый SQL быстр в dev, но медленный в prod — это устаревшая статистика, перекос данных или parameter sniffing?
Один и тот же запрос мгновенен в dev и медленный в prod. План из prod снят так:
Nested Loop (cost=.. rows=5) (actual time=41200 rows=3841002 ..)
-> Index Scan using idx_events_type on events (actual rows=3841002 ..)
-> Index Scan using users_pkey on users (actual rows=1 ..)
Planning Time: 0.3 ms
Execution Time: 41833 ms
Таблицы в prod намного больше, чем в dev. Определите, какая причина подходит и почему.
План оценил 5 строк, но Index Scan вернул 3,8 млн, и планировщик выбрал Nested Loop, выгодный лишь на нескольких строках — признак устаревшей статистики. Запустите ANALYZE, чтобы оценка стала верной и он выбрал hash join. Перекос данных — когда статистика свежа, но одно значение доминирует.
- ✗Называть это parameter sniffing, когда Postgres перепланирует в каждой сессии
- ✗Списывать всё замедление на перекос данных вопреки разрыву оценки
- ✗Форсировать Seq Scan вместо обновления статистики
- →Как подтвердить перекос данных, если ANALYZE не помог?
- →Когда generic plan в PL/pgSQL даёт эффект вроде sniffing в Postgres?
Ключ — строка (cost=.. rows=5) (actual .. rows=3841002): планировщик ждал 5 строк, а получил 3,8 млн. Nested Loop выгоден, только когда внешняя сторона даёт горстку строк — на каждую делается быстрый поиск во второй таблице по индексу. При 3,8 млн итераций это катастрофа. Заниженная в 750 000 раз оценка — прямой признак устаревшей статистики на большой prod-таблице (в dev таблица мелкая, поэтому даже плохой план быстр).
ANALYZE events;
После обновления статистики оценка станет близка к 3,8 млн, и планировщик сам переключится с Nested Loop на Hash Join. Как различить причины: устаревшая статистика — большой разрыв оценки и факта (этот случай); перекос данных — статистика свежая, но одно значение type встречается непропорционально часто; parameter sniffing — кэшированный общий план для prepared-запроса (в основном термин SQL Server; в Postgres близко к generic plan после нескольких выполнений).