I’ve used EXPLAIN ANALYZE for years without realizing how much I was missing
by not adding BUFFERS.
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE user_id = 12345;
The output now includes lines like:
Buffers: shared hit=1 read=247
shared hit=1→ 1 page came from Postgres’ shared buffer cacheread=247→ 247 pages had to be fetched (from OS cache or disk)
This matters because the same query can take 0.3 ms or 30 ms depending on
whether the data is hot. If you’re chasing tail latency, EXPLAIN ANALYZE
alone will lie to you on a warm cache.
The takeaway
When debugging slow queries, always use BUFFERS. Run the query twice:
- First run shows the cold-cache cost (
readis high) - Second run shows the warm-cache cost (
shared hitis high)
The gap between them is your storage I/O dependency. Big gap = your query is disk-bound under cold cache, which means latency spikes after restarts or cache evictions.
Annoyingly, EXPLAIN ANALYZE executes the query, so don’t run it on
DELETE / UPDATE in production unless you wrap it in a transaction and
roll back.