Выбор ответа Senior Индексы обновлено

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

Попробуйте сами

Выберите вариант, и откроется разбор.

Показать ответ и разбор

Коротко

Правильный ответ: Изменённые страницы не all-visible в visibility map, нужен VACUUM. Индекс в PostgreSQL не хранит информацию о видимости версий строк (MVCC).

Варианты ответа

  1. Index Only Scan всегда читает таблицу, чтобы вернуть колонки не из индекса
  2. Индекс не покрывает запрос, нужен INCLUDE (created_at) в индексе
  3. Изменённые страницы не all-visible в visibility map, нужен VACUUMправильный ответ
  4. Это признак повреждения индекса после 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 ошибок.