Skip to content
In this guide

Guide · The plan, node by node

Bitmap Heap Scan

A Bitmap Heap Scan is two nodes. The Bitmap Index Scan under it walks the index and, instead of fetching rows as it goes, marks every matching page in a bitmap. The Bitmap Heap Scan then reads those pages once each, in physical order, and rechecks the condition on every row. It is the planner's middle ground: too many rows for an Index Scan to fetch one at a time, too few for a Seq Scan. SELECT * FROM commerce.orders WHERE status = 'cancelled' on orders_status_placed_idx is the shape, where status has a correlation of 0.04.

Why it matters

Fine while the bitmap stays exact. It lives in work_mem, and when it outgrows that PostgreSQL stops tracking rows and tracks whole pages. The scan then reads every row on those pages, counting the ones that fail as Rows Removed by Index Recheck. EXPLAIN ANALYZE prints Heap Blocks: exact=N lossy=M, and any lossy above zero means the bitmap overflowed.

What to do

  1. If lossy is above zero, raise work_mem for the session and run again.
  2. If the recheck removes most rows and the bitmap was exact, the index condition is weaker than the filter. Add the filtered column to the index.
  3. If the scan keeps most of the table, it is a Seq Scan with extra steps. Check the estimate first. Estimated rows explains the ≈N label.

In the Plan tab

The Bitmap Index Scan is folded into its Bitmap Heap Scan, so you see one node. Its wheel matches the last of the five patterns the TABLE WHEEL guide draws.

The five ways a scan reaches a table, as the TABLE WHEEL guide draws them.

The five ways a scan reaches a table, as the TABLE WHEEL guide draws them.

  1. Seq Scan reads the whole rim. A bitmap scan that keeps most of the table ends up here with extra steps.
  2. Index Scan · in order fetches rows one at a time through the spokes, in a short arc. When the rows sit together like this, the bitmap buys little.
  3. Index Scan · scattered fetches rows one at a time across the whole rim, in index order. This is the case the bitmap is for: too many rows to fetch one by one.
  4. Index Only Scan answers from the hub and never opens the table.
  5. Bitmap Heap Scan is this node: the spokes are the page bitmap, not lookups, and the rim is read once, in page order. Lossy pages are drawn as their own rim segments, each read whole and rechecked row by row.

The finding reads Bitmap went lossy, N lossy pages rechecked, with a Retry · button carrying the next work_mem to try.

Caveats

  • A GIN index like customers_attributes_idx is always used through a bitmap. That one is not a sign of anything.

Last checked against Seqora 0.3.2, PostgreSQL 17 on .

Found a bug, missing a feature, or just curious? Write to me. Every email lands in my own inbox, not a ticket queue.