Когда Postgres сам себя обманывает (и как это починить)
Иногда индекс есть, запрос написан правильно, а база всё равно тормозит. Часто виноват «мозг» Postgres — планировщик. Это он решает, как именно читать ваши данные.
Как он думает?
Представьте, что планировщик — это навигатор. Но карт у него нет. Он ходит с биноклем (`ANALYZE`) на крышу и примерно запоминает: «Ага, в этой колонке в основном числа от 1 до 100, а всего в таблице миллион строк».
На основе этих заметок он строит маршрут.
Где он ошибается?
1. У него старые данные.
Вы закинули в таблицу миллион новых заказов, а «навигатор» об этом не знает. Он по старой памяти думает, что строк мало. И решает: «Да ну его, этот индекс, я просто пробегусь по всей таблице, это быстро».
Итог: грузит весь диск в память, всё висит.
*Чинится:* командой `ANALYZE orders;` (или просто подождать автовакуум).
2. Он боится прыгать по диску
В настройках базы (параметр `random_page_cost`) до сих пор стоит цифра `4`. Это значит: «Один прыжок в случайное место диска в 4 раза дороже, чем читать подряд».
Это правда для дискет из 90-х. У вас SSD! Для него прыжок почти ничего не стоит. Поставьте этот параметр в `1.1`. База перестанет бояться индексов и начнет ими пользоваться.
3. Вы спрятали колонку в функцию
Написали `WHERE created_at::date = '2026-01-01'`
Планировщик видит эту маску (`::date`) и говорит: «Я не знаю, что получится на выходе, поэтому индекс по `created_at` я использовать не буду».
*Как надо:* `WHERE created_at >= '2026-01-01' AND created_at < '2026-01-02'`.
Как проверить, что он там надумал?
Перед запросом напишите слово `EXPLAIN` (лучше `EXPLAIN ANALYZE`).
База покажет план. Смотрите на строчку `rows`. Если там написано `rows=10`, а на самом деле вернулось 100 000 — значит, у планировщика безнадежно устарели данные о таблице.
Иногда индекс есть, запрос написан правильно, а база всё равно тормозит. Часто виноват «мозг» Postgres — планировщик. Это он решает, как именно читать ваши данные.
Как он думает?
Представьте, что планировщик — это навигатор. Но карт у него нет. Он ходит с биноклем (`ANALYZE`) на крышу и примерно запоминает: «Ага, в этой колонке в основном числа от 1 до 100, а всего в таблице миллион строк».
На основе этих заметок он строит маршрут.
Где он ошибается?
1. У него старые данные.
Вы закинули в таблицу миллион новых заказов, а «навигатор» об этом не знает. Он по старой памяти думает, что строк мало. И решает: «Да ну его, этот индекс, я просто пробегусь по всей таблице, это быстро».
Итог: грузит весь диск в память, всё висит.
*Чинится:* командой `ANALYZE orders;` (или просто подождать автовакуум).
2. Он боится прыгать по диску
В настройках базы (параметр `random_page_cost`) до сих пор стоит цифра `4`. Это значит: «Один прыжок в случайное место диска в 4 раза дороже, чем читать подряд».
Это правда для дискет из 90-х. У вас SSD! Для него прыжок почти ничего не стоит. Поставьте этот параметр в `1.1`. База перестанет бояться индексов и начнет ими пользоваться.
3. Вы спрятали колонку в функцию
Написали `WHERE created_at::date = '2026-01-01'`
Планировщик видит эту маску (`::date`) и говорит: «Я не знаю, что получится на выходе, поэтому индекс по `created_at` я использовать не буду».
*Как надо:* `WHERE created_at >= '2026-01-01' AND created_at < '2026-01-02'`.
Как проверить, что он там надумал?
Перед запросом напишите слово `EXPLAIN` (лучше `EXPLAIN ANALYZE`).
База покажет план. Смотрите на строчку `rows`. Если там написано `rows=10`, а на самом деле вернулось 100 000 — значит, у планировщика безнадежно устарели данные о таблице.