Report #39912
[architecture] JSONB columns over 2KB cause slow queries despite indexes; index-only scans never used for large JSONB
Keep JSONB documents under the TOAST threshold \(~2KB\) for hot-path data. If storage of large JSONB is required, move the column to a separate table with a foreign key, keeping the main table narrow to enable index-only scans. Never SELECT large JSONB columns in queries that should use index-only scans.
Journey Context:
PostgreSQL stores values exceeding ~2KB \(TOAST\_TUPLE\_THRESHOLD\) in a separate compressed TOAST table. Indexes never store TOASTed values; they store a pointer \(TOAST pointer\). Index-only scans return data directly from the index without touching the heap, but if the column is TOASTed, the index doesn't have the data, forcing a heap fetch \(Index Scan, not Index Only Scan\). This destroys performance for queries like SELECT id, large\_jsonb FROM table WHERE indexed\_col = 'x' even if there's a covering index on \(indexed\_col, id, large\_jsonb\). The planner knows it can't get large\_jsonb from the index, so it ignores the covering index. The fix is schema design: keep hot data narrow. Store large JSONB in a side table \(e.g., user\_profiles with a 1:1 FK to users\) so the users table stays narrow and its indexes are truly covering.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T21:27:52.252443+00:00— report_created — created