Почему в плане Bitmap Heap Scan, а не Index Scan
Вопрос
Запрос SELECT * FROM orders WHERE customer_id = 42, есть индекс по customer_id. Таблица orders на 1 млн строк, customers на 50 тыс., PostgreSQL 16. Почему планировщик выбрал Bitmap Heap Scan?
Bitmap Heap Scan on orders (cost=4.58..81.71 rows=20 width=32) (actual time=0.080..0.189 rows=20 loops=1)
Recheck Cond: (customer_id = 42)
Heap Blocks: exact=20
-> Bitmap Index Scan on orders_customer_id_idx (cost=0.00..4.58 rows=20 width=0) (actual time=0.050..0.050 rows=20 loops=1)
Index Cond: (customer_id = 42)
Planning Time: 0.459 ms
Execution Time: 0.250 ms
Попробуйте сами
Выберите вариант, и откроется разбор.
Показать ответ и разбор
Коротко
Варианты ответа
- Индекс повреждён, и PostgreSQL перепроверяет каждую строку через
Recheck Cond - 20 строк на 20 разных страницах: карта собирает адреса и читает страницы по разуправильный ответ
- Bitmap Heap Scan выбирается для равенства, а Index Scan только для диапазонов
- Таблица не проанализирована, и планировщик не знает, сколько строк вернётся
Разбор
Bitmap-скан это компромисс между Index Scan и Seq Scan. Сначала Bitmap Index Scan собирает из индекса адреса подходящих строк в битовую карту страниц, потом Bitmap Heap Scan читает эти страницы в физическом порядке, каждую один раз. Heap Blocks: exact=20 при rows=20 говорит, что все строки лежат на разных страницах: значения customer_id в таблице не коррелируют с физическим порядком, и обычный Index Scan прыгал бы по таблице случайно. Recheck Cond не признак поломки: проверка нужна, если карта станет «lossy» (на уровне страниц, когда не хватает work_mem), здесь все блоки exact. Тип скана не зависит от вида условия: равенство прекрасно идёт через Index Scan (как поиск по первичному ключу). Статистика есть: оценка rows=20 совпала с фактом.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.
Предыдущий вопрос
Seq Scan при выборке 90% строк
Следующий вопрос
Почему COUNT(*) на большой таблице медленный