Index Only Scan с большим Heap Fetches
Вопрос
После массового UPDATE за 1 марта план стал таким. Почему Index Only Scan всё равно ходит в таблицу (Heap Fetches: 2926)?
Index Only Scan using orders_created_at_idx on orders (cost=0.42..92.12 rows=2785 width=8) (actual time=0.023..0.787 rows=2880 loops=1)
Index Cond: ((created_at >= '2024-03-01 00:00:00+00'::timestamp with time zone) AND (created_at < '2024-03-02 00:00:00+00'::timestamp with time zone))
Heap Fetches: 2926
Planning Time: 0.057 ms
Execution Time: 0.872 ms
Попробуйте сами
Выберите вариант, и откроется разбор.
Показать ответ и разбор
Коротко
Варианты ответа
- Index Only Scan всегда читает таблицу, чтобы вернуть колонки не из индекса
- Индекс не покрывает запрос, нужен
INCLUDE (created_at)в индексе - Изменённые страницы не all-visible в visibility map, нужен VACUUMправильный ответ
- Это признак повреждения индекса после UPDATE, нужен
REINDEX
Разбор
Индекс в PostgreSQL не хранит информацию о видимости версий строк (MVCC). Index Only Scan может не ходить в таблицу только для страниц, помеченных в visibility map как all-visible. UPDATE создаёт новые версии строк и снимает эту пометку, поэтому видимость проверяется в куче. После VACUUM (обычно его делает autovacuum) пометки возвращаются, и Heap Fetches падает до 0. Нюанс PostgreSQL 14+: если мёртвых строк мало, VACUUM может пропустить чистку индексов, и такие страницы остаются не all-visible; тогда помогает VACUUM (INDEX_CLEANUP ON). Запрос выбирает только created_at, так что индекс его покрывает, и INCLUDE ничего не даст. Повреждения тут нет.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.