Более привычный index scan
выдаёт следующий TID
(Tuple Identifier
) по одному строго по очереди. Получив из index scan
очередной TID
- идём проверять видимость транзакции для этой строки, доставать остальные поля и проверять остальные условия запроса, если есть. Если строка подходит по условиям - то возвращаем её на вышестоящий узел плана выполнения (или запросившей эти данные программе если выше никого нет). Затем берём из индекса следующий TID
.
bitmap index scan
сначала получает все подходящие под условие индексного поиска TID
из индекса, затем по порядку запрашивает соответствующие блоки самой таблицы. Порядок чтения физически таблицы так получается более предсказуемый, для bitmap index scan
можем использовать вызовы posix fadvise для информирования операционной системы, что нам скоро понадобятся вот такие вот блоки (это всё на что действительно влияет настройка effective_io_concurrency
), readahead вероятно тоже будет чуть более полезен чем обычно. Промежуточная структура данных, используемая для хранения TID
или целых страниц таблицы, которые нужно прочесть из таблицы и называется bitmap.
Что интересно, bitmap index scan
может быть lossy
. У индекса есть возможность вернуть строки, которые возможно подходят под условие поиска, но это нужно проверить после чтения действительных данных блока. Например, индексный метод brin
может сказать "на этой странице данных в таблице есть подходящие записи", но не сказать какие именно, он не хранит сами TID
, только номера страниц. При недостатке work_mem
bitmap может сам перейти в режим lossy
: отметить на битовой карте, что нужно прочитать все строки со страницы и отбросить лишнее - это существенно компактнее по памяти, чем отмечать на битовой карте конкретные строки.
Но при этом bitmap, конечно, вообще не способен возвращать данные в отсортированном виде. Типичный запрос показать "10 последних по дате чего бы то ни было" будет неэффективно выполнять через bitmap (вполне возможно, что seqscan будет даже удобнее), но элементарное действие для index scan
.