Рабочие заметки

Почему index-only scan не включался

Был отчётный запрос, который считает количество событий по дню. Индекс покрывающий, колонок из таблицы не нужно ни одной, а план всё равно упорно показывал Index Scan с походом в heap.

EXPLAIN (ANALYZE, BUFFERS)
SELECT day, count(*) FROM events
WHERE day >= '2026-08-01' GROUP BY day;

В плане было вот это:

Index Only Scan using events_day_idx on events
  Heap Fetches: 4118233
  Buffers: shared hit=21044 read=98311

Heap Fetches — вот и ответ

Index-only scan на самом деле включался. Просто он почти всегда ходил в heap: четыре миллиона обращений вместо нуля. Причина — visibility map. Планировщик может пропустить чтение строки только если страница отмечена как «полностью видимая», а отмечает их VACUUM.

Таблица активно писалась, а autovacuum до неё доходил редко: порог по умолчанию — двадцать процентов от размера таблицы, а таблица большая. Проверить легко:

SELECT relname, last_autovacuum, n_dead_tup, n_live_tup
FROM pg_stat_user_tables WHERE relname = 'events';

last_autovacuum был двухнедельной давности.

Что сделал

Сначала руками:

VACUUM (VERBOSE, ANALYZE) events;

После этого Heap Fetches упал до нескольких тысяч, а запрос с 8.4 с ушёл на 0.2 с. Потом поправил пороги именно для этой таблицы, чтобы не возвращаться к вопросу:

ALTER TABLE events SET (
  autovacuum_vacuum_scale_factor = 0.02,
  autovacuum_analyze_scale_factor = 0.01,
  autovacuum_vacuum_cost_delay = 2ms
);

Что стоит запомнить


← ко всем записям