October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk5 min

Postgres Indexes Under the Hood: B-Trees, Page Splits, and Why PostgreSQL Chooses a Sequential Scan

PostgreSQL may choose a sequential scan when an index would mean many scattered heap fetches. Learn how B-trees split, when fillfactor matters, and how to investigate plan estimates.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A PostgreSQL index is an option, not an instruction. The planner estimates the cost of available plans and may choose a sequential scan when it expects that reading the table directly will be cheaper than finding matching rows through an index. To understand an unexpected choice, inspect the plan and its row estimates before changing the index or trying to force a scan type.

This guide follows the PostgreSQL 18 documentation, accessed October 5, 2026. Details can change across PostgreSQL versions.

As an Amazon Associate I earn from qualifying purchases.

What an index does—and what it does not promise

An index gives PostgreSQL another route to find rows. It does not guarantee that every query with a matching condition will use that route. The planner compares alternatives using estimated costs, including how many rows are likely to match and how much table data each plan may need to read.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For a selective condition, an index can reduce the number of table pages PostgreSQL must visit. But an index scan may find matching entries scattered across the table and then require many separate heap fetches. When many rows qualify, those fetches can cost more than reading the table sequentially. A sequential scan in that situation can be a sound choice, not evidence that PostgreSQL failed to notice the index.

Which index method fits the query?

B-tree is PostgreSQL’s default index method. It suits equality and range comparisons on ordered values, including predicates such as BETWEEN and IN, and it can support sorted retrieval. Other index methods serve different indexable clauses; they are not interchangeable alternatives for every predicate.

Method What PostgreSQL 18 documentation establishes Practical implication
B-tree Supports equality and range comparisons on ordered values and can provide sorted retrieval. Consider it for those comparisons and when the query needs ordered output.
Hash Available as a distinct index method for different indexable clauses. Check whether its supported clause matches the query; do not assume it substitutes for B-tree.
GiST Available as a distinct index method for different indexable clauses. Match the method to the query’s operators and data shape.
SP-GiST Available as a distinct index method for different indexable clauses. Match the method to the query’s operators and data shape.
GIN Available as a distinct index method for different indexable clauses. Match the method to the query’s operators and data shape.
BRIN Available as a distinct index method for different indexable clauses. Match the method to the query’s operators and data shape.

For any method, verify that the index supports the operators in the actual predicate. Index choice also has workload trade-offs: compare the query pattern with write and update activity, index size, and any need for ordering. The method names alone do not establish that one is faster for a particular workload.

How a B-tree is organized

A PostgreSQL B-tree is a multi-way balanced tree built from pages, not a binary tree. Pages form levels: searches move through the tree toward the relevant leaf page, while pages at each level are linked as doubly linked lists. This organization supports both lookup and ordered traversal.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When an item cannot fit on a page, PostgreSQL can split the page. It moves some items to a new page and adds a downlink to that page in the parent. If the parent cannot fit the new downlink, that page can split too. A split can therefore cascade up the tree; if the root splits, PostgreSQL creates a new top level.

A split is a structural operation that makes room. By itself, it is not proof that the index is corrupt or unusable. PostgreSQL’s B-tree implementation attempts tuple cleanup in some circumstances before splitting, but that behavior does not guarantee that a split will be avoided.

What B-tree fillfactor changes

Fillfactor controls how full B-tree leaf pages are made during an initial build and when the index is extended at the right with new largest keys. Less-full pages leave room for later inserts, but whether that helps depends on the workload. PostgreSQL documents a default of 90 and says values from 50 to 90 may smooth early page splits for some insert or update workloads—not that they will improve every index.

Setting Documented meaning When to consider it
90 (default) PostgreSQL’s documented default B-tree fillfactor. Use as the baseline unless measurements or a known workload justify testing a change.
50–90 A documented range that may smooth early page splits in some insert/update workloads; the effect is workload-dependent. Benchmark against the actual write pattern, index size, and read performance rather than selecting a universal value.

Fillfactor is a tuning lever, not a fix for an unsuitable index method, a mismatched predicate, or inaccurate row estimates.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How to investigate an index that appears to be ignored

  1. Run EXPLAIN on the exact query. For example: EXPLAIN SELECT * FROM orders WHERE customer_id = 42; Read the scan node, estimated rows, index conditions if present, and total estimated cost. The plan shows the path PostgreSQL estimates is cheapest for that query.

  2. Check whether the predicate fits the index. Confirm the index method supports the predicate’s operators and data type, and consider whether the condition is selective enough for an index route to reduce the work.

  3. Compare estimates with execution when it is safe to run the query. EXPLAIN ANALYZE SELECT * FROM orders WHERE customer_id = 42; executes the statement and reports actual rows and timings alongside estimates. Use extra care with data-changing statements: EXPLAIN ANALYZE executes them too.

  4. If the data has changed substantially or statistics may be stale, run ANALYZE orders; and inspect the plan again. ANALYZE collects statistics about value distributions; PostgreSQL uses those statistics to estimate how many rows a condition will return. Stale or unrepresentative estimates are one possible reason for a surprising plan.

    Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  5. Weigh the likely table-page access, not just the existence of an index. If many rows qualify or the index route would lead to many scattered heap fetches, a sequential scan may still be less costly.

PostgreSQL’s documented costs are arbitrary units, not elapsed-time promises. Sample plans in the manual illustrate plan choices; their row counts and timings are not universal benchmarks, and estimates can vary with the platform and data.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Why forcing an index scan is not the default fix

Forcing index use can be useful as a controlled diagnostic: it tests what happens under an alternative plan. It does not show that the forced plan is better for production. Compare the plans and, where safe, actual execution on representative data before changing production behavior. PostgreSQL’s documentation emphasizes that index decisions depend on the workload and often require experimentation.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.