Eric Radman : a Journal

PostgreSQL Administrative Queries

These are operations that I commonly use to aid supporting PostgreSQL databases.

Updates

Disable triggers for session

SET session_replication_role = replica;

Use this in conunction with Iterative Large Updates.

Copying a Database

Dump/restore globals and one database to another server

pg_dumpall -g | ssh $host psql -f -
pg_dump -Fc $name  | ssh $host pg_restore -C -U $owner -d $name

Create a database on the same server

CREATE DATABASE test_localharvest WITH TEMPLATE localharvest;

Discovery

Find columns that are not used for a group of tables

SELECT tablename, attname, n_distinct, correlation, null_frac
FROM pg_stats
WHERE tablename ~ 'log_'
AND null_frac = 1;

Show which databases are handling large operations that are spooled to disk

SELECT datname,temp_files, temp_bytes/(2^30) AS "temp_files (GB)"
FROM pg_stat_database
WHERE temp_files > 1;

Databases listed by size

SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
ORDER BY pg_database_size(datname) DESC;

Tables by size

SELECT
  nspname AS namespace,
  pg_get_userbyid(c.relowner) AS owner,
  relname,
  pg_size_pretty(pg_table_size(c.oid)) AS table_size
FROM pg_class c
LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
AND n.nspname !~ '^pg_toast'
AND pg_relation_size(c.oid) > 10*1048576
AND c.relkind = 'r'
ORDER BY pg_total_relation_size(c.oid) DESC;

Indexes by size

SELECT
  nspname AS namespace,
  pg_get_userbyid(c.relowner) AS owner,
  relname,
  pg_size_pretty(pg_relation_size(c.oid)) AS relation_size
FROM pg_class c
LEFT JOIN pg_namespace n ON (n.oid = c.relnamespace)
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
AND n.nspname !~ '^pg_toast'
AND pg_relation_size(c.oid) > 10*1048576
AND c.relkind = 'i'
ORDER BY pg_total_relation_size(c.oid) DESC;

References: catalog-pg-class

Sum of objects sorted by size

WITH kinds AS (
  SELECT *
  FROM (VALUES
    ('r', 'ordinary table'),
    ('i', 'index'),
    ('S', 'sequence'),
    ('t', 'TOAST table'),
    ('v', 'view'),
    ('m', 'materialized view'),
    ('c', 'composite type'),
    ('f', 'foreign table'),
    ('p', 'partitioned table'),
    ('I', 'partitioned index')) AS t (relkind, description)
)
SELECT c.relkind, kinds.description,
  sum(pg_relation_size(c.oid)) AS total_size,
  pg_size_pretty(sum(pg_relation_size(c.oid))) AS total_size_pretty
FROM pg_class c
JOIN kinds ON kinds.relkind = c.relkind
WHERE pg_total_relation_size(c.oid) > 0
GROUP BY c.relkind, kinds.description
ORDER BY total_size DESC;

Show all non-default settings

SELECT name, current_setting(name)
FROM pg_settings
WHERE source NOT IN ('default', 'override');

Find tables and indexes with a large number of rows (approximate)

SELECT relname, reltuples::bigint
FROM pg_class
WHERE reltuples > 10000000
ORDER BY reltuples DESC;

List privileges by role

SELECT table_name, privilege_type, array_agg(grantee ORDER BY grantee) AS grantees
FROM information_schema.table_privileges
WHERE grantee IN ('user1', 'user2')
GROUP BY table_name, privilege_type;

System Status and Intervention

Find a query that is blocking others

SELECT pid, usename, pg_blocking_pids(pid) AS blocked_by, query AS blocked_query
FROM pg_stat_activity
WHERE cardinality(pg_blocking_pids(pid)) > 0;

Terminate all remote connections

SELECT pg_terminate_backend(pid) FROM pg_stat_activity
WHERE client_addr IS NOT NULL;

Compare backup archive with last WAL flushed

[postgres@gdb3 ~]$ pgbackrest info
stanza: main
    status: ok
    cipher: none

    db (current)
        wal archive min/max (12-2): 000000010000F34C000000B2/000000010000F34D00000010
postgres=# select pg_walfile_name(pg_current_wal_flush_lsn());

Updating Configuration

For parameters that can be updated without a server restart

SELECT pg_reload_conf();

To see host base rules

SELECT * FROM pg_hba_file_rules;

And which settings are not default

SELECT name, source, setting FROM pg_settings
WHERE source != 'default';

Insert Sample Data

Insert a random fixed-length string and a sequential id

INSERT INTO uid_mapping (uid, name)
SELECT uid, substr(md5(random()::text), 0, 10)
FROM generate_series(5000, 10000) AS uid;

Autovacuum

Determine stage and progress for autovacuum workers

SELECT pid, relid, phase,
       heap_blks_scanned::float/heap_blks_total AS "%",
       heap_blks_total, heap_blks_scanned
FROM pg_stat_progress_vacuum;
/*
initializing
scanning heap
vacuuming indexes
vacuuming heap
cleaning up indexes
truncating heap
performing final cleanup
*/

Rate-limit the I/O caused by autovacuum on a large table

ALTER TABLE files SET (autovacuum_vacuum_cost_limit = 500);
ALTER TABLE files SET (autovacuum_vacuum_cost_delay = 10);

Set back to defaults using

ALTER TABLE files RESET (autovacuum_vacuum_cost_delay, autovacuum_vacuum_cost_delay);