Что такое индекс only scan в PostgreSQL
Что проверяют
Понимаете ли вы, откуда PostgreSQL берёт данные, и умеете ли читать план запроса. За вопросом стоит навык оптимизации горячих ручек сервиса: бэкендер на Go упирается обычно не в язык, а в лишние обращения к диску.
Как отвечать
Index Only Scan это способ выполнить запрос, прочитав только индекс и не заглядывая в саму таблицу. Он возможен, когда все колонки из SELECT, WHERE и ORDER BY есть в индексе.
Дальше идёт деталь, на которой отсеиваются поверхностные ответы: версии строк в индексе не хранятся. Индексная запись не знает, видима ли строка вашей транзакции, поэтому исполнитель сверяется с картой видимости. Если страница помечена как полностью видимая, поход в таблицу не нужен; иначе строку всё равно приходится доставать. Карту заполняет VACUUM, а не ANALYZE, поэтому на таблице с активной записью, где автовакуум не поспевает, оптимизация вырождается в обычный Index Scan.
Проверяется это планом, а не секундомером:
CREATE INDEX idx_orders_user ON orders (user_id) INCLUDE (status);
EXPLAIN (ANALYZE, BUFFERS)
SELECT user_id, status FROM orders WHERE user_id = 42;Строка Heap Fetches: 0 означает, что оптимизация сработала. INCLUDE кладёт колонку в лист индекса, не делая её частью ключа сортировки.
Где спотыкаются
Говорят, что видимость строк лежит в индексе, и после этого не могут объяснить, почему сразу за массовым обновлением план прежний, а время выросло вдвое. Ответ в счётчике обращений к таблице и в отставшем автовакууме.
Сканируют результат в широкую структуру и просят у базы все колонки. Одно лишнее поле в списке выборки отменяет покрытие целиком, а в Go это особенно легко не заметить: запрос собирается строкой в одном месте, структура правится в другом.
Лепят один широкий составной индекс, чтобы покрыть все запросы сразу. Индекс раздувается, каждая запись в таблицу дорожает, и выигрыш на чтении съедается замедлением вставок.