> [!NOTE] Bitmap Heap Scan > <table> > <tr> > <td width="25%"><img src="assets/ex_bitmapheapscan.png"></td> > <td>A hybrid scan that bridges the gap between an Index Scan and a Seq Scan. It reads a 'Bitmap Index Scan' (a list of target pages) and then visits the table in physical order, which is much faster than random access if many rows are being fetched.</td> > </tr> > </table> > > ```sql > -- Consuming a bitmap to fetch data from the heap > EXPLAIN (ANALYZE, COSTS, BUFFERS, VERBOSE) > SELECT * FROM animals WHERE species_id = 1; > ``` > > ![BitmapHeapScan Plan Tree](assets/plan_tree_op_bitmap_heap_scan.svg) > > ```text > Bitmap Heap Scan on public.animals (cost=27.79..126.78 rows=2000 width=27) (actual time=0.055..0.320 rows=2000 loops=1) > Output: id, name, species_id, created_at > Recheck Cond: (animals.species_id = 1) > Heap Blocks: exact=74 > Buffers: shared hit=77 > -> Bitmap Index Scan on idx_animals_species_id (cost=0.00..27.29 rows=2000 width=0) (actual time=0.043..0.043 rows=2000 loops=1) > Index Cond: (animals.species_id = 1) > Buffers: shared hit=3 > Planning: > Buffers: shared hit=86 > Planning Time: 0.382 ms > Execution Time: 0.409 ms > ``` ![Bitmap Heap Scan measured plan performance signature](assets/trace_op_bitmap_heap_scan.svg) > > ### Physical Implementation > - **Sequential Physical I/O**: Because the bitmap is sorted by Page ID, Postgres reads the disk in order. This turns expensive Random I/O into efficient Sequential I/O. > - **The Recheck Condition**: If the child bitmap became "Lossy" due to memory pressure, Postgres only knows that a Page *might* contain valid records. It must re-verify the match condition for every tuple on that page. In `EXPLAIN` plans, this shows up as `Recheck Cond`. > - **I/O Prefetching**: Because Postgres knows the entire "route" ahead of time, it can issue **Asynchronous I/O** requests to pre-fetch the next pages before the server even arrives at them. > > --- > > - **Cost**: page fetch cost plus CPU to recheck tuples when the bitmap is lossy.