Understanding Filter vs Recheck Conditions #
When analyzing execution plans, distinguishing between Filter and Recheck conditions is critical for accurate performance diagnostics. A Filter node indicates that the database engine evaluates a predicate against rows during the initial data retrieval phase. In contrast, a Recheck condition signals that an index scan returned candidate rows, but the exact predicate match requires heap page verification. Mastering this distinction is foundational to Reading & Interpreting Query Plans and directly dictates how you approach query optimization.
Execution Flow: Filter vs Recheck Mechanics #
A Filter operation applies a WHERE clause condition during the initial data retrieval phase. It remains highly efficient when the predicate evaluates using only index entries or sequential scan buffers. The engine discards non-matching rows immediately, avoiding secondary lookups.
A Recheck condition appears when the optimizer narrows the search space via an index but cannot guarantee predicate truth without accessing the base heap tuple. This typically occurs during bitmap index scans or when memory constraints force lossy pointer tracking. Understanding Filter Pushdown Mechanics clarifies why certain predicates defer evaluation to later execution stages.
Root Causes of Recheck Conditions #
Recheck nodes are not inherently problematic, but they introduce measurable I/O overhead. Identifying the trigger allows for precise remediation. Common triggers include:
- Lossy Bitmap Scans: Insufficient memory forces the planner to track heap pages instead of exact row pointers, requiring full page scans to verify matches.
- Partial or Expression Indexes: The index omits columns required for the predicate, forcing a fallback heap lookup.
- TOASTed Columns: Large out-of-line column values require physical heap fetches before evaluation can occur.
- Index-Only Scan Fallback: Stale visibility map data or MVCC snapshot requirements force the planner to verify tuple visibility on the heap.
Diagnostic Workflow & Resolution Steps #
Follow this targeted workflow to isolate and resolve inefficient recheck patterns. Execute each step sequentially to validate tuning impact.
- Step 1: Run
EXPLAIN (ANALYZE, BUFFERS)on the target query to locate theBitmap Heap ScanorSeq Scannode displayingRecheck Cond. - Step 2: Compare
Rows Removed by FilteragainstHeap Fetches. A high fetch-to-return ratio confirms excessive page reads. - Step 3: Increase
work_memfor the session to prevent lossy bitmap conversion and restore exact pointer tracking. - Step 4: Implement composite or covering indexes to satisfy the predicate entirely within the index structure, enabling Index-Only Scans.
- Step 5: Rewrite query predicates to align with index column order, pushing evaluation earlier in the execution tree to minimize heap access.
Standard Filter on Sequential Scan #
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, status FROM orders
WHERE created_at > '2023-01-01' AND region = 'US';
Plan Analysis:
The output displays a Seq Scan node with Filter: (created_at > '2023-01-01' AND region = 'US'). The cost metric reflects startup and total CPU cycles for row evaluation. The actual time metric shows the wall-clock duration per loop iteration. No heap recheck occurs because all required columns reside in the scanned pages. If the filter removes over ninety percent of rows, the sequential scan becomes heavily CPU-bound and warrants a targeted index.
Recheck on Lossy Bitmap Scan #
EXPLAIN (ANALYZE, BUFFERS)
SELECT id, payload FROM logs
WHERE log_level = 'ERROR' AND app_name = 'auth-service';
Plan Analysis:
The plan reveals a Bitmap Index Scan feeding into a Bitmap Heap Scan with Recheck Cond: (app_name = 'auth-service'). The cost metric spikes due to additional page fetches and memory allocation. The actual time metric shows latency from lossy page verification. The engine must scan every tuple on those pages to verify the app_name predicate. High Heap Fetches combined with elevated shared buffers hit ratios confirm the I/O penalty.
Common Diagnostic Pitfalls #
Avoid these misinterpretations when tuning filter and recheck conditions:
- Assuming
Recheckis always a performance bug rather than a standard execution path for specific index types. - Ignoring
work_memlimits during bitmap scan analysis, which masks unnecessary lossy conversions. - Over-indexing without evaluating heap fetch overhead, inadvertently increasing write amplification and cache pressure.
- Misreading
Rows Removed by Filteras index inefficiency when it actually reflects accurate predicate selectivity.
Frequently Asked Questions #
Does a Recheck condition mean my index is broken or misconfigured? Not necessarily. Recheck conditions are a standard execution engine behavior, particularly for bitmap scans, partial indexes, and TOASTed data. They only indicate a problem when they cause disproportionate heap fetches relative to the final row count.
How does work_mem directly impact Filter vs Recheck behavior?
When work_mem is insufficient to store exact row pointers during a bitmap scan, PostgreSQL switches to a lossy bitmap that tracks heap pages instead. This forces a Recheck condition because the engine must scan every row on the candidate pages to verify the predicate.
Can I force the planner to avoid Recheck conditions entirely?
You can minimize them by increasing work_mem, creating covering indexes to enable Index-Only Scans, or restructuring queries to align with existing index column orders. However, completely eliminating Recheck is neither possible nor required for optimal performance across all workloads.