'Unnaturally large Postgres database size
I'm not very experienced with Postgres but I have a relatively small database (less than ~300 rows total) but it takes up more than 150 MB of storage. It grew to this from ~11 MB without much addition of new data. I was wondering what might be the cause of this and how can I fix this issue?
I ran the following queries to get an understanding of what might be happening, but being new to Postgres I cannot really find a proper fix.
SELECT SUM(pg_database_size(pg_database.datname)) / (1024 * 1024) as size_mb
FROM pg_database;
| size_mb |
| -------------------- |
| 162.0249948501586914 |
select table_schema, table_name,
pg_size_pretty(pg_total_relation_size('"'||table_schema||'"."'||table_name||'"')) as size
from information_schema.tables;
The result:
pg_class and pg_statistic take up 95 MB and 43 MB respectively.
| table_schema | table_name | size |
| ------------------ | ------------------------------------- | ---------- |
| pg_catalog | pg_statistic | 43 MB |
| pg_catalog | pg_type | 208 kB |
| pg_catalog | pg_foreign_table | 16 kB |
| pg_catalog | pg_authid | 80 kB |
| pg_catalog | pg_shadow | 0 bytes |
| pg_catalog | pg_statistic_ext_data | 16 kB |
| pg_catalog | pg_roles | 0 bytes |
| pg_catalog | pg_settings | 0 bytes |
| pg_catalog | pg_file_settings | 0 bytes |
| pg_catalog | pg_hba_file_rules | 0 bytes |
| pg_catalog | pg_config | 0 bytes |
| pg_catalog | pg_shmem_allocations | 0 bytes |
| pg_catalog | pg_prepared_statements | 0 bytes |
| pg_catalog | pg_largeobject | 8192 bytes |
| pg_catalog | pg_user_mapping | 24 kB |
| pg_catalog | pg_stat_progress_vacuum | 0 bytes |
| pg_catalog | pg_replication_origin_status | 0 bytes |
| pg_catalog | pg_subscription | 24 kB |
| pg_catalog | pg_attribute | 848 kB |
| pg_catalog | pg_proc | 1216 kB |
| pg_catalog | pg_class | 95 MB |
| pg_catalog | pg_attrdef | 80 kB |
| pg_catalog | pg_constraint | 136 kB |
| pg_catalog | pg_inherits | 16 kB |
| pg_catalog | pg_index | 112 kB |
| pg_catalog | pg_operator | 232 kB |
| pg_catalog | pg_opfamily | 80 kB |
| pg_catalog | pg_opclass | 80 kB |
| pg_catalog | pg_am | 72 kB |
| pg_catalog | pg_amop | 192 kB |
| pg_catalog | pg_amproc | 128 kB |
| pg_catalog | pg_language | 80 kB |
| pg_catalog | pg_largeobject_metadata | 8192 bytes |
| pg_catalog | pg_aggregate | 72 kB |
| pg_catalog | pg_statistic_ext | 32 kB |
| pg_catalog | pg_rewrite | 696 kB |
| pg_catalog | pg_trigger | 96 kB |
| pg_catalog | pg_event_trigger | 48 kB |
| pg_catalog | pg_description | 496 kB |
| pg_catalog | pg_cast | 80 kB |
| pg_catalog | pg_stat_progress_cluster | 0 bytes |
| pg_catalog | pg_stat_progress_create_index | 0 bytes |
| pg_catalog | pg_stat_progress_basebackup | 0 bytes |
| pg_catalog | pg_user_mappings | 0 bytes |
| pg_catalog | pg_enum | 56 kB |
| pg_catalog | pg_namespace | 80 kB |
| pg_catalog | pg_conversion | 96 kB |
| pg_catalog | pg_depend | 1240 kB |
| pg_catalog | pg_database | 80 kB |
| pg_catalog | pg_db_role_setting | 32 kB |
| pg_catalog | pg_tablespace | 80 kB |
| pg_catalog | pg_auth_members | 72 kB |
| pg_catalog | pg_shdepend | 112 kB |
| pg_catalog | pg_shdescription | 64 kB |
| pg_catalog | pg_ts_config | 72 kB |
| pg_catalog | pg_ts_config_map | 88 kB |
| pg_catalog | pg_ts_dict | 80 kB |
| pg_catalog | pg_ts_parser | 72 kB |
| pg_catalog | pg_ts_template | 72 kB |
| pg_catalog | pg_extension | 80 kB |
| pg_catalog | pg_foreign_data_wrapper | 24 kB |
| pg_catalog | pg_foreign_server | 24 kB |
| pg_catalog | pg_policy | 48 kB |
| pg_catalog | pg_replication_origin | 24 kB |
| pg_catalog | pg_default_acl | 48 kB |
| pg_catalog | pg_init_privs | 72 kB |
| pg_catalog | pg_seclabel | 16 kB |
| pg_catalog | pg_shseclabel | 16 kB |
| pg_catalog | pg_collation | 80 kB |
| pg_catalog | pg_partitioned_table | 16 kB |
| pg_catalog | pg_range | 56 kB |
| pg_catalog | pg_transform | 16 kB |
| pg_catalog | pg_sequence | 24 kB |
| pg_catalog | pg_publication | 40 kB |
| pg_catalog | pg_publication_rel | 16 kB |
| pg_catalog | pg_subscription_rel | 8192 bytes |
| pg_catalog | pg_group | 0 bytes |
| pg_catalog | pg_user | 0 bytes |
| pg_catalog | pg_policies | 0 bytes |
| pg_catalog | pg_rules | 0 bytes |
| pg_catalog | pg_views | 0 bytes |
| pg_catalog | pg_tables | 0 bytes |
| pg_catalog | pg_matviews | 0 bytes |
| pg_catalog | pg_indexes | 0 bytes |
| pg_catalog | pg_sequences | 0 bytes |
| pg_catalog | pg_stats | 0 bytes |
| pg_catalog | pg_stats_ext | 0 bytes |
| pg_catalog | pg_publication_tables | 0 bytes |
| pg_catalog | pg_locks | 0 bytes |
| pg_catalog | pg_cursors | 0 bytes |
| pg_catalog | pg_available_extensions | 0 bytes |
| pg_catalog | pg_available_extension_versions | 0 bytes |
| pg_catalog | pg_prepared_xacts | 0 bytes |
| pg_catalog | pg_seclabels | 0 bytes |
| pg_catalog | pg_statio_user_tables | 0 bytes |
| pg_catalog | pg_stat_all_indexes | 0 bytes |
| pg_catalog | pg_timezone_abbrevs | 0 bytes |
| pg_catalog | pg_timezone_names | 0 bytes |
| pg_catalog | pg_stat_sys_indexes | 0 bytes |
| pg_catalog | pg_stat_all_tables | 0 bytes |
| pg_catalog | pg_stat_xact_all_tables | 0 bytes |
| pg_catalog | pg_stat_sys_tables | 0 bytes |
| pg_catalog | pg_stat_xact_sys_tables | 0 bytes |
| pg_catalog | pg_stat_user_tables | 0 bytes |
| pg_catalog | pg_stat_xact_user_tables | 0 bytes |
| pg_catalog | pg_statio_all_tables | 0 bytes |
| pg_catalog | pg_statio_sys_tables | 0 bytes |
| pg_catalog | pg_stat_user_indexes | 0 bytes |
| pg_catalog | pg_statio_all_indexes | 0 bytes |
| pg_catalog | pg_statio_sys_indexes | 0 bytes |
| pg_catalog | pg_statio_user_indexes | 0 bytes |
| pg_catalog | pg_statio_all_sequences | 0 bytes |
| pg_catalog | pg_statio_sys_sequences | 0 bytes |
| pg_catalog | pg_statio_user_sequences | 0 bytes |
| pg_catalog | pg_stat_activity | 0 bytes |
| pg_catalog | pg_stat_replication | 0 bytes |
| pg_catalog | pg_stat_slru | 0 bytes |
| pg_catalog | pg_stat_wal_receiver | 0 bytes |
| pg_catalog | pg_stat_subscription | 0 bytes |
| pg_catalog | pg_stat_ssl | 0 bytes |
| pg_catalog | pg_stat_gssapi | 0 bytes |
| pg_catalog | pg_replication_slots | 0 bytes |
| pg_catalog | pg_stat_database | 0 bytes |
| pg_catalog | pg_stat_database_conflicts | 0 bytes |
| pg_catalog | pg_stat_user_functions | 0 bytes |
| pg_catalog | pg_stat_xact_user_functions | 0 bytes |
| pg_catalog | pg_stat_archiver | 0 bytes |
| pg_catalog | pg_stat_bgwriter | 0 bytes |
| pg_catalog | pg_stat_progress_analyze | 0 bytes |
| information_schema | columns | 0 bytes |
| information_schema | information_schema_catalog_name | 0 bytes |
| information_schema | collations | 0 bytes |
| information_schema | applicable_roles | 0 bytes |
| information_schema | administrable_role_authorizations | 0 bytes |
| information_schema | column_privileges | 0 bytes |
| information_schema | attributes | 0 bytes |
| information_schema | collation_character_set_applicability | 0 bytes |
| information_schema | character_sets | 0 bytes |
| information_schema | check_constraint_routine_usage | 0 bytes |
| information_schema | check_constraints | 0 bytes |
| information_schema | column_column_usage | 0 bytes |
| information_schema | column_domain_usage | 0 bytes |
| information_schema | column_udt_usage | 0 bytes |
| information_schema | constraint_column_usage | 0 bytes |
| extensions | pg_stat_statements | 0 bytes |
| information_schema | constraint_table_usage | 0 bytes |
| information_schema | domain_constraints | 0 bytes |
| information_schema | domain_udt_usage | 0 bytes |
| information_schema | domains | 0 bytes |
| information_schema | enabled_roles | 0 bytes |
| information_schema | sql_implementation_info | 48 kB |
| information_schema | key_column_usage | 0 bytes |
| information_schema | parameters | 0 bytes |
| information_schema | referential_constraints | 0 bytes |
| information_schema | role_column_grants | 0 bytes |
| information_schema | sql_parts | 48 kB |
| information_schema | routine_privileges | 0 bytes |
| information_schema | role_routine_grants | 0 bytes |
| information_schema | routines | 0 bytes |
| information_schema | schemata | 0 bytes |
| information_schema | tables | 0 bytes |
| information_schema | sequences | 0 bytes |
| information_schema | sql_sizing | 48 kB |
| information_schema | sql_features | 104 kB |
| information_schema | table_constraints | 0 bytes |
| information_schema | transforms | 0 bytes |
| information_schema | table_privileges | 0 bytes |
| information_schema | role_table_grants | 0 bytes |
| information_schema | udt_privileges | 0 bytes |
| information_schema | triggered_update_columns | 0 bytes |
| information_schema | triggers | 0 bytes |
| information_schema | usage_privileges | 0 bytes |
| information_schema | role_udt_grants | 0 bytes |
| information_schema | role_usage_grants | 0 bytes |
| information_schema | user_defined_types | 0 bytes |
| information_schema | view_column_usage | 0 bytes |
| information_schema | view_routine_usage | 0 bytes |
| information_schema | view_table_usage | 0 bytes |
| information_schema | foreign_tables | 0 bytes |
| information_schema | views | 0 bytes |
| information_schema | data_type_privileges | 0 bytes |
| information_schema | _pg_user_mappings | 0 bytes |
| information_schema | element_types | 0 bytes |
| information_schema | _pg_foreign_table_columns | 0 bytes |
| information_schema | column_options | 0 bytes |
| information_schema | _pg_foreign_data_wrappers | 0 bytes |
| information_schema | foreign_data_wrapper_options | 0 bytes |
| information_schema | user_mapping_options | 0 bytes |
| information_schema | foreign_data_wrappers | 0 bytes |
| information_schema | _pg_foreign_servers | 0 bytes |
| information_schema | foreign_server_options | 0 bytes |
| information_schema | foreign_servers | 0 bytes |
| information_schema | _pg_foreign_tables | 0 bytes |
| information_schema | user_mappings | 0 bytes |
| information_schema | foreign_table_options | 0 bytes |
| auth | refresh_tokens | 160 kB |
| auth | audit_log_entries | 336 kB |
| auth | instances | 16 kB |
| auth | schema_migrations | 24 kB |
| public | card_tag | 56 kB |
| public | users | 32 kB |
| public | cards | 144 kB |
| public | collections | 32 kB |
| public | tags | 32 kB |
| storage | buckets | 24 kB |
| storage | migrations | 40 kB |
| storage | objects | 32 kB |
| auth | identities | 104 kB |
| realtime | schema_migrations | 24 kB |
| realtime | subscription | 56 kB |
| auth | users | 176 kB |
Does this help narrow down the possible cause?
UPDATE 1:
Ran the following queries:
vacuum verbose pg_statistic;
vacuum verbose pg_class;
Output:
vacuuming "pg_catalog.pg_class"
launched 2 parallel vacuum workers for index cleanup (planned: 2)
"pg_class": found 0 removable, 358975 nonremovable row versions in 8160 out of 8160 pages
vacuuming "pg_catalog.pg_statistic"
"pg_statistic": found 0 removable, 122120 nonremovable row versions in 5330 out of 5330 pages
vacuuming "pg_toast.pg_toast_2619"
"pg_toast_2619": found 0 removable, 98 nonremovable row versions in 22 out of 22 pages
SELECT pg_stat_statements_reset();
SELECT pg_stat_reset();
The database size hasn't decreased. However, I had added two new rows into one of my tables and the size increased from 162 MB to now 163.67 MB.
UPDATE 2:
select count(*) from pg_class
| count |
|---|
| 469 |
UPDATE 3:
Table schemas:
cards
| column_name | data_type | character_maximum_length | column_default | is_nullable |
| ------------- | --------- | ------------------------ | -------------- | ----------- |
| id | bigint | | | NO |
| user_id | uuid | | | NO |
| excerpt | text | | | YES |
| note | text | | | YES |
| collection_id | bigint | | | YES |
| created_at | date | | now() | NO |
| tags | ARRAY | | | NO |
| fts | tsvector | | | YES |
tags
| column_name | data_type | character_maximum_length | column_default | is_nullable |
| ----------- | --------- | ------------------------ | -------------- | ----------- |
| id | bigint | | | NO |
| user_id | uuid | | | NO |
| name | text | | | NO |
| colour | character | 6 | | NO |
collections
| column_name | data_type | character_maximum_length | column_default | is_nullable |
| ----------- | --------- | ------------------------ | -------------- | ----------- |
| id | bigint | | | NO |
| name | text | | | NO |
| link | text | | | YES |
| user_id | uuid | | | NO |
| author | text | | | YES |
card_tag
| column_name | data_type | character_maximum_length | column_default | is_nullable |
| ----------- | --------- | ------------------------ | -------------- | ----------- |
| card_id | bigint | | | NO |
| tag_id | bigint | | | NO |
| user_id | uuid | | | NO |
users
| column_name | data_type | character_maximum_length | column_default | is_nullable |
| ----------- | --------- | ------------------------ | -------------- | ----------- |
| id | uuid | | | NO |
| email | text | | | YES |
| customer_id | text | | | YES |
UPDATE 4:
fts column in cards table is generated as:
ALTER TABLE cards
ADD COLUMN fts tsvector generated always as (to_tsvector('english', excerpt || ' ' || note)) stored;
fts is also indexed.
create index cards_fts on cards using gin (fts);
UPDATE 5:
vacuum verbose pg_class;
vacuuming "pg_catalog.pg_class"
launched 1 parallel vacuum worker for index cleanup (planned: 1)
"pg_class": found 0 removable, 358987 nonremovable row versions in 8160 out of 8160 pages
select pg_column_size(to_tsvector('english', excerpt || ' ' || note)) from cards;
| pg_column_size |
| -------------- |
| 80 |
| 196 |
| 68 |
| 514 |
| 198 |
| 32 |
| 32 |
| 78 |
| 186 |
| 62 |
| 370 |
| 34 |
| 436 |
| 540 |
| 314 |
| 560 |
| 20 |
| 46 |
| 472 |
| 136 |
| 124 |
| 84 |
| 22 |
| 592 |
| 22 |
| 318 |
| 54 |
| 516 |
| 54 |
| 250 |
| 106 |
| 76 |
| 296 |
| 450 |
| 38 |
| 34 |
| 178 |
| |
| 170 |
UPDATE 6:
select relname ,pg_stat_get_live_tuples(c.oid) AS live_tuples ,pg_stat_get_dead_tuples(c.oid) AS dead_tuples from pg_class c order by dead_tuples desc
| relname | live_tuples | dead_tuples |
| ----------------------------------------------- | ----------- | ----------- |
| pg_class | 469 | 358518 |
| pg_attribute | 0 | 126 |
| pg_depend | 0 | 10 |
| pg_type | 0 | 7 |
| pg_shdepend | 225 | 6 |
| users | 1 | 4 |
| refresh_tokens | 6 | 3 |
| card_tag | 5 | 2 |
| pg_db_role_setting | 7 | 1 |
| cards | 3 | 1 |
| pg_index | 0 | 1 |
| identities | 1 | 1 |
| pg_authid | 18 | 1
UPDATE 7:
select pid, state, usename, query, query_start from pg_stat_activity where pid in ( select pid from pg_locks l join pg_class t on l.relation = t.oid and t.relkind = 'r' where t.relname in ('pg_class','cards', 'tags', 'collections', 'card_tag', 'users') );
| pid | state | usename | query | query_start |
| ----- | ------ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| 79331 | active | supabase_admin |
-- source: dashboard
-- user: 20248
-- date: 2022-04-08T15:02:13.664Z
select pid, state, usename, query, query_start from pg_stat_activity where pid in ( select pid from pg_locks l join pg_class t on l.relation = t.oid and t.relkind = 'r' where t.relname in ('pg_class','cards', 'tags', 'collections', 'card_tag', 'users') );
| 2022-04-08 15:02:14.123713+00 |
select version()
| version |
|---|
| PostgreSQL 13.3 on aarch64-unknown-linux-gnu, compiled by gcc (Ubuntu 10.3.0-1ubuntu1~20.04) 10.3.0, 64-bit |
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
