Skip to content
In this guide

Guide · The plan, node by node

Buffers

Buffers is the line EXPLAIN (ANALYZE, BUFFERS) adds under each node, counting 8 kB pages. shared hit is pages found in shared_buffers, PostgreSQL's own cache. shared read is pages fetched from the operating system. dirtied and written are pages the query changed and pages it wrote out to make room. temp read and temp written are the temp files a Sort or Hash Join spilled to. The sample's Seq Scan on commerce.orders prints Buffers: shared hit=1204 read=181796.

Why it matters

Time varies with what else the server is doing. Buffers do not, which makes them the figure to compare between two runs of a query. The ratio is read against hit. orders had 181,796 of its 183,000 pages come from outside shared_buffers, which is why the scan took 1,640 ms, and why it is faster the second time. Few rows and many buffers is an index on a low-correlation column, a new page per row. Any temp above zero means work_mem ran out somewhere below. written on a SELECT is not a bug: a read evicts dirty pages and sets hint bits.

What to do

  1. Touch fewer pages before caching more. Reaching 206,880 rows through an index touches the pages those rows are on, not all 183,000. Will this index help? shows how to test one.
  2. If read dominates on a table queried all day, shared_buffers may be too small for the working set. orders alone is 1.5 GB.
  3. If temp is above zero, find the node that spilled. Its finding names it.

In the Plan tab

The inspector's Pages section is the Buffers line drawn. Marker 3 is the one this page is about.

The inspector for Seq Scan on orders, with Pages third from the top.

The inspector for Seq Scan on orders, with Pages third from the top.

  1. Rows is read against kept, 2% kept. Rows and pages tell the same story from two sides: 12,000,000 rows is 183,000 pages.
  2. Rows per loop is actual against estimated, 338× more. Pages are not estimated, so there is no ruler for them.
  3. Pages is shared hit against shared read, drawn as cache 1,204 against disk 182K, 99% from disk. The table was not in shared_buffers, and temp would appear beside them if the node had spilled.
  4. Indexes · orders is the way to touch fewer pages: an index that starts with channel would reach the kept rows' pages and skip the rest.
  5. suggested is that index, with Test. The cost Test prints counts pages in units of one sequential read, so fewer pages touched is what a lower cost means.

all cached replaces the percentage when nothing came from disk. Dirtied and written pages are not drawn. They are in Raw plan JSON.

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.