VACUUM PostgreSQL Database
On this page
Solution
After running the STA server for several months, the EC2 may have enough disk space due to bloated database tables. You can use PostgreSQL VACUUM
command to return space to the OS.
Here is the step by step process:
- Login to the STA EC2 Instance
- Check the disk usage
df -h
- Login to the PostgreSQL Database
sudo su postgrespsql -d sensorthings
- Check the last time your tables were vacuumed, analyzed, live and dead tup
SELECT relname, n_live_tup, n_dead_tup, last_vacuum, last_autovacuumFROM pg_stat_all_tablesORDER BY n_dead_tup/(n_live_tup* current_setting('autovacuum_vacuum_scale_factor')::float8+ current_setting('autovacuum_vacuum_threshold')::float8)DESCLIMIT 12;Results:relname | n_live_tup | n_dead_tup | last_vacuum---------------------+------------+------------+-------------------------------pg_class | 367 | 75 | 2018-09-05 22:28:22.941349+00pg_statistic | 499 | 58 | 2018-09-05 22:11:16.729493+00pg_index | 150 | 23 | 2018-09-05 22:28:22.95605+00pg_toast_2619 | 32 | 16 | 2018-09-05 22:11:16.730219+00pg_attribute | 2734 | 128 | 2018-09-05 22:28:22.874761+00pg_type | 411 | 27 | 2018-09-05 22:11:16.829871+00location | 2660 | 55 | 2018-09-05 22:11:16.73436+00pg_depend | 9388 | 177 | 2018-09-05 22:28:23.072771+00data_stream | 2660 | 45 | 2018-09-05 22:28:22.737992+00observation | 56042655 | 172199 | 2018-09-13 21:20:39.68152+00feature_of_interest | 2350 | 6 | 2018-09-05 22:28:22.778667+00(11 rows)
If your bloated table does not show up here, n_dead_tup is zero and last_autovacuum is NULL, you might have a problem with the statistics collector.
If the bloated table is right there on top, but last_autovacuum is NULL, you might need to configure autovacuum to be more aggressive so that it gets done with the table.
- Vacuum the table
The observation
table usually is the biggest table in the database. It has the largest dead tuples as well.
BE CAREFUL. VACUUM FULL
is only needed when you have a table that is mostly dead rows - i.e after unnecessary historical data has been deleted by the cron task. It should not be used for table optimization or periodic maintenance, as it's generally counterproductive.
- Plain
VACUUM
: Deletes dead tuples and frees up space for re-use
VACUUM observation;
Full VACUUM
: Locks the database table, and reclaims more space than a plain but doesn't delete dead tuples
VACUUM FULL observation;
Notice:
VACUUM FULL
is much slower than a normalVACUUM
, so the table may be unavailable for a while. You might need extra volume to executeVACUUM FULL
command. Because it requires extra disk space for the new copy of the table until the operation completes. To do that:
- Login to your AWS Web console
- Go to EC2
- Select the instance that you want to extend
- Go to the mounted boot volume
- Click Modify Volume
- Change the size field to whatever value you need
- Click the Modify button to apply the changes
- Finally, restart the EC2 instance that links to the EBS volume.
Notice:The size of a volume can only be increased, not decreased.
- Set up autovacuum
To prevent our tables from continually getting messy in the future and having to manually VACUUM
and ANALYZE
, you can make the default auto-vacuum settings stricter. Postgres
runs a daemon to regularly vacuum and analyze itself.
Tables are auto-vacuumed when 20% of the rows plus 50 rows are inserted, updated or deleted, and auto-analyzed similarly at 10%, and 50-row thresholds. These settings work fine for smaller tables, but as a table grows to have millions of rows, there can be tens of thousands of inserts or updates before the table is vacuumed and analyzed.
Below is an example which will VACUUM
 and ANALYZE
 after 5,000 inserts, updates, or deletes.
ALTER TABLE observation SET (autovacuum_vacuum_scale_factor = 0.0);ALTER TABLE observation SET (autovacuum_vacuum_threshold = 5000);ALTER TABLE observation SET (autovacuum_analyze_scale_factor = 0.0);ALTER TABLE observation SET (autovacuum_analyze_threshold = 5000);
The threshold to auto-vacuum
is calculated by:
vacuum threshold = autovacuum_vacuum_threshold + autovacuum_vacuum_scale_factor * number of rows in table
Similarly, the threshold to auto-analyze is calculated by:
analyze threshold = autovacuum_analyze_threshold + autovacuum_analyze_scale_factor * number of rows in table
However, autovacuum sometimes doesn't free the dead tuples.
You can verify the problem by running VACUUM(VERBOSE)
VACUUM (VERBOSE) observation;
Why we need VACUUM
When you perform UPDATE
and DELETE
operations on a table in Postgres, the database has to keep around the old row data for concurrently running queries and transactions, due to its MVCC model. Once all concurrent transactions that have seen these old rows have finished, they effectively become dead rows which will need to be removed.
VACUUM is the process by which PostgreSQL cleans up these dead rows, and turns the space they have occupied into usable space again, to be used for future writes.
A more detailed description can be found in the PostgreSQL documentation.
Which tables have VACUUM running?
The easiest thing you can check on a running PostgreSQL system is which VACUUM operations are running right now. In all Postgres versions this information shows up in the pg_stat_activity
 view, look for query values that start with "autovacuum: ", or which contain the word "VACUUM":
SELECT pid, query FROM pg_stat_activity WHERE query LIKE 'autovacuum: %';
VACUUM metrics to monitor
In order to ensure that VACUUMs are running smoothly across your databases, you should monitor:
Why won’t VACUUM remove the dead rows?
VACUUM can only remove those row versions (also known as “tuples”) that are not needed any more. A tuple is not needed if the transaction ID of the deleting transaction (as stored in the xmax system column) is older than the oldest transaction still active in the PostgreSQL database (or the whole cluster for shared tables).
This value is called the xmin horizon
.
There are three things that can hold back this xmin horizon in a PostgreSQL cluster:
1.Long-running transactions:
You can find those and their xmin value with the following query:
SELECT pid, datname, usename, state, backend_xminFROM pg_stat_activityWHERE backend_xmin IS NOT NULLORDER BY age(backend_xmin) DESC;
You can use the pg_terminate_backend() function to terminate the database session that is blocking your VACUUM.
2.Abandoned replication slots:
A replication slot is a data structure that keeps the PostgreSQL server from discarding information that is still needed by a standby server to catch up with the primary.
If replication is delayed or the standby server is down, the replication slot will prevent VACUUM from deleting old rows.
You can find all replication slots and their xmin value with this query:
SELECT slot_name, slot_type, database, xminFROM pg_replication_slotsORDER BY age(xmin) DESC;
Use the pg_drop_replication_slot() function to drop replication slots that are no longer needed.
Note: This can only happen with physical replication if hot_standby_feedback = on. For logical replication there is a similar hazard, but only system catalogs are affected. Examine the column catalog_xmin in that case.
3.Orphaned prepared transactions:
During two-phase commit, a distributed transaction is first prepared with the PREPARE statement and then committed with the COMMIT PREPARED statement.
Once a transaction has been prepared, it is kept “hanging around” until it is committed or aborted. It even has to survive a server restart! Normally, transactions don’t remain in the prepared state for long, but sometimes things go wrong and a prepared transaction has to be removed manually by an administrator.
You can find all prepared transactions and their xmin value with the following query:
SELECT gid, prepared, owner, database, transaction AS xminFROM pg_prepared_xactsORDER BY age(transaction) DESC;
Use the ROLLBACK PREPARED SQL statement to remove prepared transactions.
ANALYZE
ANALYZE
 gathers statistics for the query planner to create the most efficient query execution paths. Per PostgreSQL documentation, accurate statistics will help the planner to choose the most appropriate query plan, and thereby improve the speed of query processing.
Example In the example below, [tablename] is optional. Without a table specified, ANALYZE will be run on available tables in the current schema that the user has access to.
ANALYZE VERBOSE [tablename]
REINDEX
The REINDEX command rebuilds one or more indices, replacing the previous version of the index. REINDEX can be used in many scenarios, e.g., an index has become "bloated".
Examples Any of these can be forced by adding the keyword FORCE after the command
- Recreate a single index, myindex:
REINDEX INDEX myindex
- Recreate all indices in a table, observation:
REINDEX TABLE observation;
- Recreate all indices in sensorthings:
REINDEX DATABASE sensorthings;
Some useful PostgreSQL commands
Show tables
\dtResults:List of relationsSchema | Name | Type | Owner--------+---------------------+-------+----------public | data_stream | table | postgrespublic | feature_of_interest | table | postgrespublic | historical_location | table | postgrespublic | location | table | postgrespublic | observation | table | postgrespublic | observation_archive | table | postgrespublic | observed_property | table | postgrespublic | sensor | table | postgrespublic | spatial_ref_sys | table | postgrespublic | thing | table | postgrespublic | thing_location | table | postgres(11 rows)
- Check database/table disk usage
Table disk usage
Tracking the amount of disk space used by each table is important because it enables you to gauge expected changes in query performance over time—but it can also help you detect potential vacuuming-related issues.
The following query shows you the table that is using the most disk space in your database:
- Check database disk usage
SELECT pg_size_pretty(pg_database_size('sensorthings'));
- Check each table disk usage
SELECTrelname AS "table_name",pg_size_pretty(pg_table_size(C.oid)) AS "table_size"FROMpg_class CLEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)WHERE nspname NOT IN ('pg_catalog', 'information_schema') AND nspname !~ '^pg_toast' AND relkind IN ('r')ORDER BY pg_table_size(C.oid)DESC LIMIT 11;Results:table_name | table_size---------------------+------------observation | 4156 MBdata_stream | 8056 kBspatial_ref_sys | 3984 kBfeature_of_interest | 2248 kBlocation | 1864 kBthing | 1672 kBhistorical_location | 168 kBthing_location | 120 kBsensor | 16 kBobserved_property | 16 kBobservation_archive | 8192 bytes
Check dead rows
SELECT relname, n_dead_tupFROM pg_stat_user_tablesORDER BY n_dead_tup DESC;Results:relname | n_dead_tup---------------------+------------observation | 176199location | 55data_stream | 45feature_of_interest | 6sensor | 0observation_archive | 0thing | 0thing_location | 0observed_property | 0spatial_ref_sys | 0historical_location | 0