Что такое индекс only scan в PostgreSQL
Что проверяют
Знаете ли вы, что происходит с запросом ниже уровня ORM. Питонист редко пишет SQL руками, и вопрос отделяет тех, кто читает план, от тех, кто верит в правило «добавили индекс, стало быстро».
Как отвечать
Index Only Scan это режим, при котором планировщик берёт всё нужное из индекса и не читает страницы таблицы. Условие одно: каждая колонка, упомянутая в выборке, условиях и сортировке, лежит в индексе.
Но индекс не хранит информацию о том, какие версии строк видны текущей транзакции. Поэтому PostgreSQL сверяется с картой видимости: страницу, помеченную вакуумом как полностью видимую, читать не надо, остальные всё равно придётся достать. Счётчик Heap Fetches в EXPLAIN (ANALYZE, BUFFERS) показывает, сколько раз пришлось.
Главная ловушка питониста в том, что ORM по умолчанию тянет все поля модели, и покрытия не выходит никогда:
Order.objects.filter(user_id=42) # SELECT всех колонок
Order.objects.filter(user_id=42).values_list("status", flat=True)В SQLAlchemy тот же смысл у load_only и with_entities. Покрывающий индекс собирается через INCLUDE: ключ остаётся узким, а нужные для выборки колонки просто лежат рядом в листе.
Где спотыкаются
Считают, что режим включается сам, раз индекс создан. Проверяют по времени ответа, а не по плану, и делают вывод наугад: ускорение могло прийти от кэша, а не от покрытия.
Оставляют ORM выбирать все поля и удивляются ненулевым обращениям к таблице. Ограничить список колонок это ровно половина работы, вторая половина в том, чтобы этот список совпал с составом индекса.
Ждут эффекта на таблице с потоком обновлений. Пока вакуум не прошёл по страницам, карта видимости пуста, и выигрыша нет вообще, сколько бы правильных колонок в индексе ни лежало.