Normally you don't have to worry about that at all.

However, if there has been a mass delete or update, or the sustained change rate was so high that autovacuum couldn't keep up, you may end up with a badly bloated index.

The tool to determine that id the pgstattuple extension:

CREATE EXTENSION pgstattuple;

Then you can examine index bloat like this:

SELECT * FROM pgstatindex('spatial_ref_sys_pkey');

-[ RECORD 1 ]------+-------
version            | 2
tree_level         | 1
index_size         | 196608
root_block_no      | 3
internal_pages     | 1
leaf_pages         | 22
empty_pages        | 0
deleted_pages      | 0
avg_leaf_density   | 64.48
leaf_fragmentation | 13.64

This index is in excellent shape (never used): It has only 14% bloat.

Mind that indexes are by default created with a fillfactor of 90, that is, index blocks are not filled to more than 90% by INSERT.

It is hard to say when an index is bloated, but if leaf_fragmentation exceeds 50-60, it's not so pretty.

To reorganize an index, use REINDEX.

Answer from Laurenz Albe on Stack Overflow
🌐
DEV Community
dev.to › dm8ry › how-to-check-fragmentation-of-tables-and-indexes-in-postgresql-fg7
How to check fragmentation of tables and indexes in PostgreSQL? - DEV Community
April 10, 2023 - postgres=# SELECT * FROM pgstatindex('cities_pkey'); version | tree_level | index_size | root_block_no | internal_pages | leaf_pages | empty_pages | deleted_pages | avg_leaf_density | leaf_fragmentation ---------+------------+------------+---------------+----------------+------------+-------------+---------------+------------------+-------------------- 4 | 0 | 16384 | 1 | 0 | 1 | 0 | 0 | 1.77 | 0 (1 row) postgres=#
🌐
MinervaDB
minervadb.xyz › home › how to troubleshoot index fragmentation in postgresql?
Troubleshooting Index Fragmentation in PostgreSQL: A Practical Guide
March 12, 2023 - Learn how to troubleshoot index fragmentation in PostgreSQL by using tools like pg_stat_user_indexes and REINDEX to identify and resolve fragmentation issues for improved query performance.
🌐
Medium
medium.com › @dmitry.romanoff › how-to-check-the-fragmentation-of-tables-and-indexes-in-postgresql-4da59dcb1704
How to check the fragmentation of tables and indexes in PostgreSQL? | by Dmitry Romanoff | Medium
November 9, 2023 - postgres=# SELECT * FROM pgstatindex('cities_pkey'); version | tree_level | index_size | root_block_no | internal_pages | leaf_pages | empty_pages | deleted_pages | avg_leaf_density | leaf_fragmentation ---------+------------+------------+---------------+----------------+------------+-------------+---------------+------------------+-------------------- 4 | 0 | 16384 | 1 | 0 | 1 | 0 | 0 | 1.77 | 0 (1 row) postgres=#
🌐
SmartTechWays
smarttechways.com › home › how to check index fragmentation in postgresql
How to Check Index Fragmentation in PostgreSQL - SmartTechWays - Innovative Solutions for Smart Businesses
3 weeks ago - This fragmentation can hinder performance by requiring more time to traverse the index. To check for index fragmentation, the pg_stat_all_indexes view can be utilized, which provides statistics about each index in the database, including the number of blocks, tuples, and the last vacuumed timestamp.
🌐
PostgreSQL Wiki
wiki.postgresql.org › wiki › Index_Maintenance
Index Maintenance - PostgreSQL wiki
*/ CASE WHEN btpo_next != 0 THEN first_item END AS highkey, /* * distinct_block_pointers is table blocks that are pointed to by items on * the page (not including high key, which doesn't point anywhere). * * This is interesting on leaf pages, because it indicates how fragmented the * index is with respect to table accesses, which is important for range * queries.
🌐
Hashnode
postgresqlblog.hashnode.dev › how-to-fix-fragmented-indexes-in-postgresql
Fixing Fragmented Indexes in PostgreSQL
May 20, 2024 - One approach is to use the pgstattuple extension, which provides statistical information about tables and indexes. By running the pgstattuple function on the table with fragmented indexes, you can gather information about the level of fragmentation.
🌐
Medium
medium.com › @jramcloud1 › reindexing-in-postgresql-17-the-complete-dba-guide-to-keeping-your-indexes-healthy-20d0cd8e828f
Reindexing in PostgreSQL 17: The Complete DBA Guide to Keeping Your Indexes Healthy | by Jeyaram Ayyalusamy | Medium
July 2, 2025 - It requires understanding of index health, table access patterns, and locking behaviors, especially in production systems. In this comprehensive PostgreSQL 17 reindexing guide, we’ll cover everything a modern DBA or developer needs to know: You’ll learn why fragmentation builds up, how to detect bloated indexes, and what performance issues it causes.
Find elsewhere
🌐
Fatdba
fatdba.com › 2018 › 09 › 04 › fragmentation-or-bloating-in-postgresql-how-to-identify-and-fix-it-using-db-vacuuming
Fragmentation Or Bloating in PostgreSQL – How to identify and fix it using DB Vacuuming
July 1, 2020 - SELECT current_database(), schemaname, tablename, /*reltuples::bigint, relpages::bigint, otta,*/ ROUND((CASE WHEN otta=0 THEN 0.0 ELSE sml.relpages::FLOAT/otta END)::NUMERIC,1) AS tbloat, CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::BIGINT END AS wastedbytes, iname, /*ituples::bigint, ipages::bigint, iotta,*/ ROUND((CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages::FLOAT/iotta END)::NUMERIC,1) AS ibloat, CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes FROM ( SELECT schemaname, tablename, cc.reltuples, cc.relpages, bs, CEIL((cc.reltuples*((datahdr+m
🌐
DEV Community
dev.to › shiviyer › troubleshooting-fragmented-indexes-in-postgresql-ho3
Troubleshooting Fragmented Indexes in PostgreSQL - DEV Community
January 26, 2024 - One approach is to use the pgstattuple extension, which provides statistical information about tables and indexes. By running the pgstattuple function on the table with fragmented indexes, you can gather information about the level of fragmentation.
🌐
Make DBA Life Easy
dbalifeeasy.com › 2020 › 10 › 04 › how-to-identify-fragmentation-in-postgresql-rds
How to Identify Fragmentation in PostgreSQL RDS – Make DBA Life Easy
November 4, 2020 - Fragmentation is a popular topic for any databases like Oracle and Microsoft SQL server. This post demonstrates how to identify fragmentation in PostgreSQL including AWS PostgreSQL RDS. As super user, install extension pgstattuple as following if not yet. ... Check extension details by query pg_extension and pg_available_extensions.
Top answer
1 of 1
9

Fragmentation is often called bloat in PostgreSQL. It relates to its implementation of MVCC where rows are not updated in place or directly deleted, but are copied with a different ID. Those rows are then made visible or invisible depending on the transaction looking at the data. You can start looking at the wiki Show database bloat for more information on fragmentation issues. Bearing in mind that this query will not give you exact numbers, it's a useful tool to have:

SELECT
  current_database(), schemaname, tablename, /*reltuples::bigint, relpages::bigint, otta,*/
  ROUND((CASE WHEN otta=0 THEN 0.0 ELSE sml.relpages::FLOAT/otta END)::NUMERIC,1) AS tbloat,
  CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::BIGINT END AS wastedbytes,
  iname, /*ituples::bigint, ipages::bigint, iotta,*/
  ROUND((CASE WHEN iotta=0 OR ipages=0 THEN 0.0 ELSE ipages::FLOAT/iotta END)::NUMERIC,1) AS ibloat,
  CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes
FROM (
  SELECT
    schemaname, tablename, cc.reltuples, cc.relpages, bs,
    CEIL((cc.reltuples*((datahdr+ma-
      (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::FLOAT)) AS otta,
    COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages,
    COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::FLOAT)),0) AS iotta -- very rough approximation, assumes all cols
  FROM (
    SELECT
      ma,bs,schemaname,tablename,
      (datawidth+(hdr+ma-(CASE WHEN hdr%ma=0 THEN ma ELSE hdr%ma END)))::NUMERIC AS datahdr,
      (maxfracsum*(nullhdr+ma-(CASE WHEN nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2
    FROM (
      SELECT
        schemaname, tablename, hdr, ma, bs,
        SUM((1-null_frac)*avg_width) AS datawidth,
        MAX(null_frac) AS maxfracsum,
        hdr+(
          SELECT 1+COUNT(*)/8
          FROM pg_stats s2
          WHERE null_frac<>0 AND s2.schemaname = s.schemaname AND s2.tablename = s.tablename
        ) AS nullhdr
      FROM pg_stats s, (
        SELECT
          (SELECT current_setting('block_size')::NUMERIC) AS bs,
          CASE WHEN SUBSTRING(v,12,3) IN ('8.0','8.1','8.2') THEN 27 ELSE 23 END AS hdr,
          CASE WHEN v ~ 'mingw32' THEN 8 ELSE 4 END AS ma
        FROM (SELECT version() AS v) AS foo
      ) AS constants
      GROUP BY 1,2,3,4,5
    ) AS foo
  ) AS rs
  JOIN pg_class cc ON cc.relname = rs.tablename
  JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname = rs.schemaname AND nn.nspname <> 'information_schema'
  LEFT JOIN pg_index i ON indrelid = cc.oid
  LEFT JOIN pg_class c2 ON c2.oid = i.indexrelid
) AS sml
ORDER BY wastedbytes DESC;

Once you know where bloat is happening, you can take measures to clean the table. The operation is called VACUUMing and can be launched on demand. It is also usually done in the background with the autovacuum worker, so please do not disable it.

As noted in https://www.postgresql.org/support/versioning/, PostgreSQL 9.1 has been unsupported for one year, consider upgrading.

🌐
Dbrnd
dbrnd.com › 2016 › 12 › postgresql-how-to-check-table-fragmentation-using-pgstattuple-module-remove-dead-tuple-vacuum-analyze
PostgreSQL: How to check Table Fragmentation using pgstattuple module
December 22, 2016 - You can find information like ... command. You must install the pgstattuple to find tuples related information. You can also use pgstatindex() to find information related to indexes......
🌐
SingleStore
singlestore.com › blog › product › understanding postgresql’s data fragmentation problem, and how singlestoredb is better
Understanding PostgreSQL’s Data Fragmentation Problem, and How SingleStoreDB Is Better
April 28, 2023 - Let's talk about PostgreSQL first, and look at the fragmentation problem it has: Fragmentation is a database server feature that allows you to control where data is stored at the table level. Fragmentation enables you to define groups of rows or index keys within a table according to some algorithm ...
🌐
Ktexperts
ktexperts.com › data-fregmentation-in-postgresql
Data Fragmentation in PostgreSQL – KTEXPERTS
December 28, 2021 - Step3: Query pg_stat_all_tables to check the details on live and dead tuples. Any rows inserted in the table will be shown as live tuples as its live data under a table and any rows that you delete will be shown under dead tuples.
🌐
Rockdata
rockdata.net › tutorial › check-indexes-to-rebuild
PostgreSQL Tutorial: Check which indexes to rebuild - Redrock Postgres
Summary: in this tutorial, you will learn how to check which indexes to rebuild in PostgreSQL. ... While it may be rare to rebuild an Btree index for performance reasons, there are some databases that will get a measurable performance boost from rebuilding indexes. These workloads have these characteristics: High index fragmentation: The SQL workload has lots of table DML causing lots of deleted leaf blocks.
🌐
PostgreSQL
postgresql.org › docs › 9.3 › pgstattuple.html
PostgreSQL: Documentation: 9.3: pgstattuple
November 8, 2018 - test=> SELECT * FROM pgstatindex('pg_cast_oid_index'); -[ RECORD 1 ]------+------ version | 2 tree_level | 0 index_size | 16384 root_block_no | 1 internal_pages | 0 leaf_pages | 1 empty_pages | 0 deleted_pages | 0 avg_leaf_density | 54.27 leaf_fragmentation | 0
Top answer
1 of 2
1

I think leaf_fragmentation is the percentage of leaf pages where the following leaf page has a lower block number. This is relevant for performance, because for an index range scan on a fragmented index, the kernel won't detect a sequential scan, and its read-ahead mechanism is defied.

But for me, the more important column is avg_leaf_density. If that is too low, your index is bloated and inefficient.

The question what values are OK is a difficult one. I'd say that anything over 30% is fine, but 20% is not terrible either.

If you are unsure whether to rebuild an index or not, ask yourself the following:

  • If you observe the bloat over time, does it continuously increase? If yes, that is bad, and a REINDEX is probably necessary.

  • If the bloat stays high, but does not increase, REINDEX once and observe

    • Does that improve the performance of your application?

    • Does the index get bloated again until it is as bloated as before?

    "No" to the second question would suggest to leave the index alone.

If you decide to REINDEX, explore if more aggressive autovacuum settings on the affected table can keep the bloat down.

2 of 2
2

It Depends™. Fragmentation by itself isn't good or bad, it's just a property of the index. If all your queries scan the index for a unique value, they will read a handful of pages, at most, in any case, so it doesn't matter if the index is fragmented. If, on the other hand, you routinely scan wide ranges of pages, lots of empty pages would probably hurt. You'll need to look at the I/O statistics to decide what's acceptable.