Start with the show database bloat sample query on the PostgreSQL wiki if you're investigating possible table/index bloat issues.

Also check whether autovacuum is enabled. Some people misguidedly turn it down or off because they see it creating load; they should actually be turning it up in these situations.

Answer from Craig Ringer on Stack Exchange
Top answer
1 of 5
8

Start with the show database bloat sample query on the PostgreSQL wiki if you're investigating possible table/index bloat issues.

Also check whether autovacuum is enabled. Some people misguidedly turn it down or off because they see it creating load; they should actually be turning it up in these situations.

2 of 5
7

If you are administrator/developer of the database, some symptoms are when you have huge (>1m rows) tables with temp data that are regularly deleted (like deleting the entries for the last month) and the database/table size does not change.

I'm using these queries to check the table size:

/***********************
* DB schema sizes
*************************/
SELECT pg_size_pretty(sum(pg_relation_size(pg_class.oid))::bigint), nspname,
CASE pg_class.relkind WHEN 'r' THEN 'table' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 'v' THEN 'view' WHEN 't' THEN 'toast' ELSE pg_class.relkind::text END
FROM pg_class
LEFT OUTER JOIN pg_namespace ON (pg_namespace.oid = pg_class.relnamespace)
GROUP BY pg_class.relkind, nspname
ORDER BY sum(pg_relation_size(pg_class.oid)) DESC;


/***********************
* DB table sizes
*************************/
SELECT pg_size_pretty(pg_relation_size(pg_class.oid)), pg_class.relname,     pg_namespace.nspname,
CASE pg_class.relkind WHEN 'r' THEN 'table' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 'v' THEN 'view' WHEN 't' THEN 'TOAST' ELSE pg_class.relkind::text END
FROM pg_class 
LEFT OUTER JOIN pg_namespace ON (pg_namespace.oid = pg_class.relnamespace)
ORDER BY pg_relation_size(pg_class.oid) DESC;

This is not 100% relevant to your question but can give you a start.

🌐
Datadog
datadoghq.com › blog › postgresql-vacuum-monitoring
PostgreSQL VACUUM Processes: How to Monitor | Datadog
November 28, 2025 - If track_counts is off, the statistics collector won’t update the count of the number of dead rows for each table, which is the value that the autovacuum daemon checks in order to determine when and where it needs to run. You can enable autovacuuming and track_counts by editing these settings in your PostgreSQL configuration file, as described in the documentation. If it looks like the autovacuum process is running in your database, but it hasn’t launched on a table that received a lot of updates that should have triggered a VACUUM, it’s possible that autovacuuming was disabled at some point on the table(s) in question.
Discussions

PostgreSQL - Query to determine which tables are currently being vacuumed? - Stack Overflow
I've found a query to view when vacuums have run, but not which are currently running. (http://heatware.net/databases/postgres-tables-auto-vacuum-analyze/) Is there a query to accomplish this? I k... More on stackoverflow.com
🌐 stackoverflow.com
optimization - how to automatically determine which tables need a vacuum / reindex in postgresql - Stack Overflow
However, if i did understood right your needs, that's what i'll do: For every table (how many tables do you have?) define the criterias; For example, talbe 'foo' need to be reindex every X new records and vacuum every X update, delete or insert · Write out your own application to do that. Every day it check ... More on stackoverflow.com
🌐 stackoverflow.com
postgresql - How to find tables that require VACUUM FULL? - Stack Overflow
Before VACUUM FULL I want to know, which table really needs this operation. To check this I found this query: SELECT tuple_percent, dead_tuple_count, dead_tuple_percent, free_space, free_percent FROM More on stackoverflow.com
🌐 stackoverflow.com
Is it common to need to do regular full vacuum on tables?
The short answer is no - it isn't common to need to do a regular full vacuum. If you're finding that this is necessary, then probably you need to tune autovacuum. In my opinion the autovacuum defaults are far too conservative, especially on larger or busier tables, but as in all things this really depends on your workload. Look into the topic of database bloat (probably starting in the PostgreSQL wiki , but there are many blog posts on this topic), and start monitoring your tables to see if this is what's happening to you. If you're seeing bloat increase, then tuning autovacuum may be necessary - although there are other potential causes of bloat as well. More on reddit.com
🌐 r/PostgreSQL
33
8
September 20, 2024
🌐
PostgreSQL
postgresql.org › docs › current › sql-vacuum.html
PostgreSQL: Documentation: 18: VACUUM
May 14, 2026 - July 16, 2026: PostgreSQL 19 Beta 2 Released! ... Unsupported versions: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1 · VACUUM — garbage-collect and optionally analyze a database · VACUUM [ ( option [, ...] ) ] [ table_and_columns [, ...] ] where option can be one of: FULL [ boolean ] FREEZE [ boolean ] VERBOSE [ boolean ] ANALYZE [ boolean ] DISABLE_PAGE_SKIPPING [ boolean ] SKIP_LOCKED [ boolean ] INDEX_CLEANUP { AUTO | ON | OFF } PROCESS_MAIN [ boolean ] PROCESS_TOAST [ boolean ] TRUNCATE [ boolean ] PARALLEL integer SKIP_DATABASE_STATS [ boolean ] ONLY_DATABASE_STATS [ boolean ] BUFFER_USAGE_LIMIT size and table_and_columns is: [ ONLY ] table_name [ * ] [ ( column_name [, ...] ) ]
Top answer
1 of 2
7

This problem can easily be solved with system catalogs. I suggest to join on pg_locks since autovacuum acquires a ShareUpdateExclusiveLock lock on the table it is working on, to avoid some manual parsing of the query from pg_stat_activity.

The following query lists the tables being auto-vacuumed, solving the pg_toast reference if a toast table is being vacuumed, as explained in Postgres pg_toast in autovacuum - which table? question linked to by @Zeki.

SELECT n.nspname || '.' || c.relname
    FROM pg_namespace n, pg_stat_activity a, pg_locks l, pg_class c
    WHERE
        a.query LIKE 'autovacuum: %'
        AND l.pid = a.pid
        AND l.mode = 'ShareUpdateExclusiveLock'
        AND (c.oid = l.relation OR c.reltoastrelid = l.relation)
        AND n.oid = c.relnamespace
        AND n.nspname <> 'pg_toast';

Please note that while pg_stat_activity and pg_locks catalogs are shared across databases, this query will only list the tables being auto-vacuumed in the current database as pg_relation is not a shared catalog.

2 of 2
2

In instead of finding if the tables are being vacuumed turn auto vacuum off for the involved tables:

alter table table_name_pattern 
set (
    autovacuum_enabled = false,
    toast.autovacuum_enabled = false
);

table pattern is a glob pattern like tbl*. At the end of the query turn auto vacuum back on

alter table table_name_pattern 
set (
    autovacuum_enabled = true,
    toast.autovacuum_enabled = true
);

Edit in response to the commentaries:

The query to find if the involved tables are being vacuumed is unnecessary and useless. If it is known that one or more of the involved tables are being vacuumed what is it supposed to be done? Wait and keep repeating the inquiring query until none is being vacuumed? And when none is then start the long query just to discover after a while that auto vacuum was just kicked off again? There is no point. Why not just turn auto vacuum off and avoid all the hassle?

There is no ethical superiority in doing it the hard way, especially if the hard way is going to give worse results than the simple one. Simpler code is simpler to use and understand but it is not necessarily easier to build. Many times it is the opposite, requiring more intellectual effort or preparedness then the complex one.


If the autovacuum setting is altered inside a transaction and that transaction is rolled back the setting will be back to whatever it was before the transaction start

drop table if exists t;
create table t (id int);

begin;

alter table t
set (
    autovacuum_enabled = false,
    toast.autovacuum_enabled = false
);

\d+ t
                          Table "public.t"
 Column |  Type   | Modifiers | Storage | Stats target | Description 
--------+---------+-----------+---------+--------------+-------------
 id     | integer |           | plain   |              | 
Has OIDs: no
Options: autovacuum_enabled=false

rollback;

\d+ t
                          Table "public.t"
 Column |  Type   | Modifiers | Storage | Stats target | Description 
--------+---------+-----------+---------+--------------+-------------
 id     | integer |           | plain   |              | 
Has OIDs: no

But that setting inside the transaction will not be seen outside of the transaction so I guess autovacuum will still run. If this is true than the setting must be done outside of the transaction and controlled by a job that will turn it back regardless of what happens with the long running query.

🌐
PostgreSQL
postgresql.org › docs › current › routine-vacuuming.html
PostgreSQL: Documentation: 18: 24.1. Routine Vacuuming
May 14, 2026 - A maximum of autovacuum_max_workers worker processes are allowed to run at the same time. If there are more than autovacuum_max_workers databases to be processed, the next database will be processed as soon as the first worker finishes. Each worker process will check each table within its database and execute VACUUM and/or ANALYZE as needed.
🌐
Medium
medium.com › gett-engineering › scaling-postgresql-check-your-vacuum-f03092a6399e
Scaling PostgreSQL: Check your Vacuum! | by Stas Panchenko | Gett Tech | Medium
March 28, 2019 - First, we wanted to check if Auto Vacuum is running on our Postgres: ... When we executed these two queries, we discovered that Postgres’ auto vacuum process that runs on dead rows in the background causes a severe performance drop for the entire database. Basically, whenever we updated or deleted a row from a Postgres table, the row was simply marked as deleted, but it wasn’t actually deleted.
🌐
Amazon Web Services
docs.aws.amazon.com › amazon rds › user guide › amazon rds for postgresql › common dba tasks for amazon rds for postgresql › working with postgresql autovacuum on amazon rds for postgresql › determining if the tables in your database need vacuuming
Determining if the tables in your database need vacuuming - Amazon Relational Database Service
For information on creating a process ... RDS for PostgreSQL ... For busier tables, perform a manual vacuum freeze regularly during a maintenance window, in addition to relying on autovacuum. For information on performing a manual vacuum freeze, see Performing a manual vacuum freeze. ... Thanks for letting us know we're doing a good job! If you've got a moment, please tell us what we did right so we can do more of it. ... Thanks for letting us know this page needs ...
Find elsewhere
🌐
Reddit
reddit.com › r/postgresql › is it common to need to do regular full vacuum on tables?
r/PostgreSQL on Reddit: Is it common to need to do regular full vacuum on tables?
September 20, 2024 -

After an incident just a week ago with a large table that refused to use an index (full vacuum solved that), ran into a problem where a 130k row table was having drastically different responses based on the value of the query against an indexed column (as long as 2 seconds depending on the value, vs. 002 for others). This time we did a full vacuum on every table and performance is better through.

And now I'm hearing this may be commonly necessary. Is that true? The standard vacuum doesn't help but full locks the table so everything needs to be shut down. Just seems odd to me that a database should require this kind maintenance

🌐
pganalyze
pganalyze.com › blog › exploring-postgres-vacuum-with-vacuum-simulator
Exploring Postgres VACUUM with the VACUUM Simulator
December 4, 2023 - The autovacuum launcher will periodically check if tables need VACUUM or not, and once it detects that a certain table exceeds the threshold, it’ll trigger an autovacuum worker to run VACUUM on that table.
🌐
PostgreSQL
postgresql.org › docs › 9.5 › routine-vacuuming.html
PostgreSQL: Documentation: 9.5: Routine Vacuuming
February 11, 2021 - Both of these kinds of whole-table scans will occur even if autovacuum is nominally disabled. PostgreSQL has an optional but highly recommended feature called autovacuum, whose purpose is to automate the execution of VACUUM and ANALYZE commands. When enabled, autovacuum checks for tables that ...
🌐
PostgreSQL
postgresql.org › docs › 9.5 › sql-vacuum.html
PostgreSQL: Documentation: 9.5: VACUUM
February 11, 2021 - The parenthesized syntax was added in PostgreSQL 9.0; the unparenthesized syntax is deprecated. ... Selects "full" vacuum, which can reclaim more space, but takes much longer and exclusively locks the table. This method also requires extra disk space, since it writes a new copy of the table and doesn't release the old copy until the operation is complete. Usually this should only be used when a significant amount of space needs ...
🌐
EnterpriseDB
enterprisedb.com › blog › postgresql-vacuum-and-analyze-best-practice-tips
PostgreSQL VACUUM Guide and Best Practices | EDB
When does autovacuum run for Postgres? chevron_right · PostgreSQL runs an autovacuum daemon process that periodically wakes up to check if any tables require vacuuming or analyzing.
🌐
Carbonblack
community.carbonblack.com › t5 › Knowledge-Base › EDR-How-to-find-Postgres-tables-to-vacuum › ta-p › 92098
Broadcom
July 6, 2020 - Note: Since your browser does not support Javascript, you must press the Continue button once to proceed
🌐
PTC Community
community.ptc.com › t5 › IoT-Tips › How-to-check-whether-a-table-or-an-index-need-a-vacuum-in › ta-p › 817675
How to check whether a table or an index need a vacuum in postgresql database used with ThingWorx
April 2, 2018 - If the number of those logically deleted records increases, PostgreSQL needs to access many pages of the table to obtain records which meets the criteria · user might experience slow performance because of this · Those logically deleted records will be ultimately removed from pages when vacuum is ...
🌐
Citus Data
citusdata.com › blog › 2022 › 07 › 28 › debugging-postgres-autovacuum-problems-13-tips
Debugging Postgres autovacuum problems: 13 tips - Citus Data
If you see bloat growing more than ... often enough. You can check when and how frequently tables were vacuumed by checking pg_stat_user_tables....
🌐
PostgreSQL
postgresql.org › docs › 9.1 › routine-vacuuming.html
PostgreSQL: Documentation: 9.1: Routine Vacuuming
October 27, 2016 - PostgreSQL has an optional but highly recommended feature called autovacuum, whose purpose is to automate the execution of VACUUM and ANALYZE commands. When enabled, autovacuum checks for tables that have had a large number of inserted, updated ...
Top answer
1 of 1
5

Firstly, I would suggest using pgstattuple to obtain tuple-level statistics.

pgstattuple returns a relation's physical length, percentage of “dead” tuples, and other info. This may help users to determine whether vacuum is necessary or not.

For example:

create extension pgstattuple ;
create table my_table ( id int , name text);
insert into my_table select a, md5(a::text) from generate_series(1, 1e7)a;

-- size of my_table
 Schema |   Name   | Type  |  Owner   |  Size  | Description
--------+----------+-------+----------+--------+-------------
 public | my_table | table | postgres | 651 MB |

-- dead_tuple_percent = 0, pgstattuple will not lock your table
SELECT tuple_percent, dead_tuple_count, dead_tuple_percent, free_space, free_percent FROM pgstattuple('my_table');

 tuple_percent | dead_tuple_count | dead_tuple_percent | free_space | free_percent
---------------+------------------+--------------------+------------+--------------
         89.35 |                0 |                  0 |     338776 |         0.05

-- let update 50% rows
update my_table set name = name || id where id < 5000000;

-- now, dead_tuple_percent = 28.63%
 tuple_percent | dead_tuple_count | dead_tuple_percent | free_space | free_percent
---------------+------------------+--------------------+------------+--------------
         60.43 |          4999999 |              28.63 |    1834236 |         0.17

-- size of my_table has increased

 Schema |   Name   | Type  |  Owner   |  Size   | Description
--------+----------+-------+----------+---------+-------------
 public | my_table | table | postgres | 1016 MB |                

-- try to vacuum full
vacuum full my_table;

-- after that, dead_tuple_percent = 0 and size of my_table has reduced
 tuple_percent | dead_tuple_count | dead_tuple_percent | free_space | free_percent
---------------+------------------+--------------------+------------+--------------
         88.92 |                0 |                  0 |    1664780 |         0.23
 Schema |   Name   | Type  |  Owner   |    Size    | Description
--------+----------+-------+----------+------------+-------------
 public | my_table | table | postgres | 691 MB     |

Secondly, if you are in production environment, I would suggest using pg_repack to reclaim disk without locking your table.

pg_repack is a PostgreSQL extension which lets you remove bloat from tables and indexes, and optionally restore the physical order of clustered indexes. Unlike CLUSTER and VACUUM FULL it works online, without holding an exclusive lock on the processed tables during processing. pg_repack is efficient to boot, with performance comparable to using CLUSTER directly.

For instance:

/usr/pgsql-11/bin/pg_repack -d postgres -U postgres -n -t my_table  &
🌐
Medium
medium.com › @anujkhandelwal0411 › how-to-check-auto-vacuum-setting-and-check-fragmentation-on-table-a7873a5c8d65
How to check auto-vacuum setting and check fragmentation on Table
December 28, 2023 - edb=# select * from pg_stat_all_tables where relname='emp'; -[ RECORD 1 ]-------+--------------------------------- relid | 16404 schemaname | public relname | emp seq_scan | 3 seq_tup_read | 100000102 idx_scan | 2 idx_tup_fetch | 2 n_tup_ins | 50000051 n_tup_upd | 0 n_tup_del | 1000000 n_tup_hot_upd | 0 n_live_tup | 49000140 n_dead_tup | 1000000 n_mod_since_analyze | 1000000 last_vacuum | last_autovacuum | last_analyze | last_autoanalyze | 28-DEC-23 09:49:12.411106 +07:00 vacuum_count | 0 autovacuum_count | 0 analyze_count | 0 autoanalyze_count | 2 · Apart from PostgreSQL having built-in features, there are also extensions available to analyze fragmentation on the table. You can check the extension details by querying ‘pg_extension’ and ‘pg_available_extensions’. As super user, install extension pgstattuple as following if not yet.