Планувальник обирає план з найменшою оцінною вартістю. Seq Scan часто справді дешевший, ніж здається.
Типові причини:
- Умова не відсіює більшість рядків. Якщо запит поверне 30% таблиці, послідовне читання сторінок підряд дешевше, ніж тисячі випадкових переходів індекс → таблиця. Так задумано.
- Маленька таблиця. Кілька сторінок простіше прочитати повністю.
- Застаріла статистика. Після масового імпорту чи видалення планувальник думає, що в таблиці 100 рядків, а їх мільйон. Рішення -
ANALYZE orders(autovacuum робить це сам, але із запізненням). - Умова не підходить до індексу: функція над колонкою (
lower(email)), неявне приведення типів,LIKE '%текст', умова на другу колонку складеного індексу без першої. - Параметризований запит з generic-планом, оптимальним «в середньому», але не для конкретного значення.
- Налаштування вартості:
random_page_cost = 4за замовчуванням розраховане на HDD. На SSD його зазвичай знижують до 1.1-1.5, і індекси стають «дешевшими» для планувальника.
Як розібратися:
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE status = 'pending';
Порівняти rows (оцінку) з actual rows. Якщо вони розходяться в рази - проблема в статистиці. Для перевірки гіпотези в сесії можна тимчасово заборонити послідовне читання: SET enable_seqscan = off; - і подивитися, наскільки «індексний» план насправді швидший. На проді так не залишають.
Нерівномірні дані: для колонок з перекосом (99% status = 'done') допомагає збільшити деталізацію статистики: ALTER TABLE orders ALTER COLUMN status SET STATISTICS 1000; і повторний ANALYZE.