Скинь сам запрос и оба плана (тестовый и продовый). Без этого гадание на кофейной гуще.
Но если в общих чертах, чек лист при расхождении планов:
1. Статистика. Да, ты делал ANALYZE. Но проверь default_statistics_target для конкретных колонок. Если данные с неравномерным распределением (например, 90% строк имеют status='active'), дефолтного значения 100 может не хватить. Попробуй:
ALTER TABLE your_table ALTER COLUMN problem_column SET STATISTICS 1000;
ANALYZE your_table;
После этого посмотри pg_stats для этой колонки:
SELECT attname, n_distinct, most_common_vals, most_common_freqs, correlation
FROM pg_stats
WHERE tablename = 'your_table' AND attname = 'problem_column';
2. Корреляция. Если correlation близка к 0, данные физически разбросаны по диску рандомно. Планировщик видит это и выбирает bitmap scan вместо index scan, потому что при низкой корреляции index scan генерирует random I/O, а bitmap собирает страницы пачкой.
Решение: CLUSTER your_table USING your_index; (но это блокирует таблицу, на проде аккуратно).
3. work_mem. На проде и тесте одинаковый? Если на тесте work_mem=256MB, а на проде дефолтный 4MB, планировщик будет избегать hash join и сортировок в памяти.
4. effective_cache_size и random_page_cost. Если на проде random_page_cost=4 (дефолт для HDD), а данные на SSD, планировщик переоценивает стоимость random I/O и избегает index scan. Для SSD ставь random_page_cost = 1.1.
5. Принудительная смена плана без хинтов. В постгресе нет хинтов (есть расширение pg_hint_plan, но это другая история). Зато можно отключать методы доступа для дебага:
SET enable_bitmapscan = off;
EXPLAIN ANALYZE SELECT ...;
Это покажет альтернативный план и его реальную стоимость. Не для прода, только для диагностики.
6. auto_explain. Включи расширение auto_explain на проде с auto_explain.log_min_duration = '1s'. Оно будет писать в лог полные планы всех запросов медленнее 1 секунды. Поймаешь деградацию в реальном времени.
Пошел проверять statistics_target и random_page_cost. Про корреляцию вообще не думал, отличная наводка. Отпишусь по результатам.