ES008: Lossy bitmap or heavy recheck
- Signal: a
Bitmap Heap Scanwith lossy heap blocks, taking at least 5% of the runtime. - Evidence: lossy and exact blocks, rows removed by the recheck.
- Action: raise
work_memso that the bitmap stays exact. - Stays silent when: rows are rechecked without any lossy blocks. That comes from lossy operator classes (trigram indexes, for example), which
work_memdoes not help.
Example
The bitmap_lossy scenario of the test corpus, captured on PostgreSQL 16: bitmap heap scan over most of the table with a tiny work_mem, so the bitmap becomes lossy and rows are rechecked.
SELECT sum(amount) FROM orders
WHERE created_at >= timestamptz '2024-01-01 00:00:00+00'
AND created_at < timestamptz '2025-07-01 00:00:00+00';
Aggregate (cost=8663.35..8663.36 rows=1 width=32) (actual time=58.985..58.988 rows=1 loops=1)
Output: sum(amount)
Buffers: shared hit=2454
-> Bitmap Heap Scan on public.orders (cost=3031.54..8288.93 rows=149768 width=6) (actual time=5.351..34.510 rows=149877 loops=1)
Output: id, customer_id, status, created_at, amount, note
Recheck Cond: ((orders.created_at >= '2024-01-01 00:00:00+00'::timestamp with time zone) AND (orders.created_at < '2025-07-01 00:00:00+00'::timestamp with time zone))
Rows Removed by Index Recheck: 14472
Heap Blocks: exact=532 lossy=1549
Buffers: shared hit=2454
-> Bitmap Index Scan on orders_created_at_idx (cost=0.00..2994.10 rows=149768 width=0) (actual time=5.263..5.264 rows=149877 loops=1)
Index Cond: ((orders.created_at >= '2024-01-01 00:00:00+00'::timestamp with time zone) AND (orders.created_at < '2025-07-01 00:00:00+00'::timestamp with time zone))
Buffers: shared hit=373
Settings: work_mem = '64kB', enable_seqscan = 'off', enable_indexscan = 'off', max_parallel_workers_per_gather = '0'
Planning:
Buffers: shared hit=98
Planning Time: 0.431 ms
Execution Time: 59.097 ms
explainsql reports:
HIGH ES008 Lossy bitmap or heavy recheck
Bitmap Heap Scan on orders kept only page numbers for 1,549 of 2,081 pages, so it rechecked every row on them.
Heap blocks: 1,549 lossy, 532 exact
Rows removed by the recheck: 14,472
Time in the node: 29.2 ms (49% of the runtime)
→ Raise work_mem for this query so that the bitmap stays exact.