Зачем
Представьте SELECT count(*) по таблице в десятки гигабайт. Читай он
страницы в обычном порядке через весь кеш - вытеснил бы оттуда всё
полезное, набив одноразовыми данными, которые после прохода не нужны.
Один отчёт - и кеш холодный для всех.
Как устроено
Чтобы этого не было, большие последовательные сканы используют не весь кеш, а маленькое буферное кольцо (ring buffer) - фиксированный небольшой набор буферов, который переиспользуется по кругу. Прочитал страницу, обработал, в том же кольце читаешь следующую. Основной кеш не трогается.
Кольца разного размера у разных операций:
- большое последовательное чтение (bulk read) - порядка 256 КБ;
- VACUUM (см. vacuum) - своё кольцо (в PG16+ настраивается через
vacuum_buffer_usage_limit); - массовая запись (
COPY, bulk write) - больше, порядка 16 МБ.
Кольцо для чтения включается, когда таблица больше четверти
shared_buffers.
Практическое следствие
Большой отчёт может быть медленным каждый раз - его данные намеренно не оседают в кеше, чтобы не вымыть полезное. Это не баг, а защита кеша.
Объединение сканов
Если две сессии одновременно последовательно сканируют одну большую
таблицу, PostgreSQL может пристроить вторую к идущему скану первой - они
читают страницы с одной позиции «паровозиком», один проход по диску
обслуживает оба. Управляется параметром synchronize_seqscans.
Кольцо - часть логики buffer-cache; на состояниях буфера (см.
buffer-states) это видно как стабильно высокий read даже после
«прогрева».