// br8dba.com β script library
PostgreSQL DBA Scripts
39 production-ready scripts for monitoring, performance tuning, vacuum, replication, backup, and administration. All scripts are tested on PostgreSQL 14β17. Click any script to expand. Use the search to filter.
PostgreSQL 14β17
39 Scripts
6 Categories
Copy-Ready
Production Safe
β
No scripts match your search.Monitoring
Sessions, wait events, blocking, cache, connections
9 scripts
01 Β· Active Sessions Overview
Full session list with state, duration, query, and wait events from pg_stat_activity.
Monitor+
SELECT pid, usename, datname, state, wait_event_type, wait_event, now() - xact_start AS xact_duration, now() - query_start AS query_duration, left(query, 100) AS query_snippet, client_addr, application_name FROM pg_stat_activity WHERE pid != pg_backend_pid() ORDER BY xact_start NULLS LAST;
π‘Filter by state:
WHERE state = 'active' for running queries, state = 'idle in transaction' for stuck transactions.02 Β· Long Running Queries
Find queries running longer than N minutes β critical for identifying hangs and performance issues.
Monitor+
-- Change '5 minutes' to your threshold SELECT pid, usename, datname, state, wait_event_type, wait_event, now() - query_start AS query_duration, now() - xact_start AS xact_duration, left(query, 200) AS query FROM pg_stat_activity WHERE state != 'idle' AND (now() - query_start) > interval '5 minutes' AND pid != pg_backend_pid() ORDER BY query_duration DESC;
β To kill a long-running query:
SELECT pg_cancel_backend(pid); β or force-terminate: SELECT pg_terminate_backend(pid);03 Β· Wait Event Summary
Real-time aggregated view of what all active sessions are waiting for β the #1 performance diagnostic.
Monitor+
-- Run this repeatedly every few seconds to see patterns SELECT COALESCE(wait_event_type, 'CPU / Running') AS wait_type, COALESCE(wait_event, 'β') AS wait_event, COUNT(*) AS sessions, STRING_AGG(pid::text, ', ') AS pids FROM pg_stat_activity WHERE state != 'idle' AND pid != pg_backend_pid() GROUP BY wait_event_type, wait_event ORDER BY sessions DESC;
π‘High
IO:DataFileRead β missing index or shared_buffers too small. High Lock:relation β table lock contention. CPU/Running β queries actively executing.04 Β· Blocking Sessions
Shows which sessions are blocking others β who is the blocker, who is blocked, and the queries involved.
Monitor+
SELECT blocked.pid AS blocked_pid, blocked.usename AS blocked_user, blocked.wait_event_type AS blocked_wait_type, left(blocked.query, 80) AS blocked_query, blocking.pid AS blocking_pid, blocking.usename AS blocking_user, left(blocking.query, 80) AS blocking_query, now() - blocked.query_start AS blocked_duration FROM pg_stat_activity AS blocked JOIN pg_stat_activity AS blocking ON blocking.pid = ANY(pg_blocking_pids(blocked.pid)) WHERE cardinality(pg_blocking_pids(blocked.pid)) > 0 ORDER BY blocked_duration DESC; -- Terminate the blocker (replace PID) -- SELECT pg_terminate_backend(blocking_pid);
π¨In Oracle: use v$session / v$lock. In PostgreSQL: pg_blocking_pids() is the key function β it returns all PIDs blocking the given PID.
05 Β· Database & Table Sizes
Disk usage breakdown β database totals, top tables by size including indexes and TOAST.
Monitor+
-- All databases with sizes SELECT datname, pg_size_pretty(pg_database_size(datname)) AS db_size FROM pg_database ORDER BY pg_database_size(datname) DESC; -- Top 20 tables by total size (table + indexes + TOAST) SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS total_size, pg_size_pretty(pg_relation_size(schemaname||'.'||tablename)) AS table_size, pg_size_pretty(pg_indexes_size(schemaname||'.'||tablename)) AS index_size FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema') ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC LIMIT 20;
06 Β· Connection Statistics
Connection usage vs max_connections, breakdown by user, database, and state.
Monitor+
-- Connection usage summary SELECT current_setting('max_connections')::int AS max_conn, COUNT(*) AS used_conn, current_setting('max_connections')::int - COUNT(*) AS free_conn, round(COUNT(*) * 100.0 / current_setting('max_connections')::int, 1) AS pct_used FROM pg_stat_activity; -- Connections per user and database SELECT usename, datname, state, COUNT(*) AS conn_count FROM pg_stat_activity WHERE pid != pg_backend_pid() GROUP BY usename, datname, state ORDER BY conn_count DESC;
β Alert if pct_used > 80%. Deploy PgBouncer if consistently high. Each connection consumes ~5β10MB RAM.
07 Β· Buffer Cache Hit Ratio
Per-database and per-table cache hit ratios β indicates whether shared_buffers is sized correctly.
Performance+
-- Database-level cache hit ratio SELECT datname, blks_hit, blks_read, round(blks_hit * 100.0 / NULLIF(blks_hit + blks_read, 0), 2) AS cache_hit_pct FROM pg_stat_database WHERE datname = current_database() ORDER BY cache_hit_pct; -- Per-table cache hit (find cold tables) SELECT schemaname, relname, heap_blks_hit, heap_blks_read, round(heap_blks_hit * 100.0 / NULLIF(heap_blks_hit + heap_blks_read, 0), 1) AS hit_pct FROM pg_statio_user_tables WHERE heap_blks_read > 0 ORDER BY heap_blks_read DESC LIMIT 20;
π‘Target: cache hit ratio > 99%. If lower, consider increasing shared_buffers (25% of RAM is a good starting point). Reset stats with
SELECT pg_stat_reset();08 Β· XID Age β Wraparound Monitor
Critical monitor: tracks transaction ID age per database and table. Alert before approaching 2.1 billion limit.
Critical+
-- XID age per database (alert if > 1.5 billion) SELECT datname, age(datfrozenxid) AS xid_age, round(age(datfrozenxid)::numeric / 2100000000 * 100, 1) AS pct_toward_limit, current_setting('autovacuum_freeze_max_age') AS freeze_max_age FROM pg_database ORDER BY xid_age DESC; -- Tables with oldest XID (find the freeze bottleneck) SELECT schemaname, relname, age(relfrozenxid) AS table_xid_age, pg_size_pretty(pg_relation_size(oid)) AS size FROM pg_class WHERE relkind = 'r' ORDER BY age(relfrozenxid) DESC LIMIT 15; -- Force freeze on a specific table -- VACUUM FREEZE VERBOSE mytable;
π¨CRITICAL: If XID age reaches ~2.1 billion without VACUUM FREEZE, PostgreSQL will refuse all connections with "database is not accepting commands to avoid wraparound data loss." Alert at 1.5B, emergency at 1.8B.
09 Β· Top SQL (pg_stat_statements)
Find the heaviest queries by total time, average time, and call count β the first stop for query performance analysis.
Performance+
-- Requires: shared_preload_libraries = 'pg_stat_statements' -- CREATE EXTENSION pg_stat_statements; -- Top 15 by total execution time SELECT round(total_exec_time::numeric, 2) AS total_ms, calls, round(mean_exec_time::numeric, 2) AS avg_ms, round(stddev_exec_time::numeric, 2) AS stddev_ms, rows, round(total_exec_time / sum(total_exec_time) OVER() * 100, 1) AS pct_total, left(query, 100) AS query_snippet FROM pg_stat_statements WHERE calls > 10 ORDER BY total_exec_time DESC LIMIT 15; -- Top by average execution time (worst per-call queries) SELECT round(mean_exec_time::numeric, 2) AS avg_ms, calls, left(query, 120) AS query FROM pg_stat_statements WHERE calls > 5 ORDER BY mean_exec_time DESC LIMIT 15;
π‘Reset statistics after tuning:
SELECT pg_stat_statements_reset(); β Oracle equivalent: V$SQL / V$SQLSTATS.Performance
Bloat, indexes, table stats, seq scans
7 scripts
10 Β· Table Bloat Detection
Identify tables with high dead tuple ratios β primary candidates for VACUUM or pg_repack.
Maintenance+
SELECT schemaname, relname AS table_name, n_live_tup, n_dead_tup, round(n_dead_tup * 100.0 / NULLIF(n_live_tup + n_dead_tup, 0), 1) AS dead_pct, pg_size_pretty(pg_relation_size(schemaname||'.'||relname)) AS table_size, last_autovacuum, last_vacuum, last_autoanalyze FROM pg_stat_user_tables WHERE n_dead_tup > 1000 ORDER BY dead_pct DESC, n_dead_tup DESC LIMIT 20; -- Action: if dead_pct > 10%, run: -- VACUUM ANALYZE schemaname.table_name; -- Or for disk reclaim (LOCKS table!): VACUUM FULL table_name; -- Production safe: pg_repack -d mydb -t table_name
β dead_pct > 10β15% = action needed. dead_pct > 30% = urgent. Use
pg_repack instead of VACUUM FULL in production to avoid exclusive locks.11 Β· Index Bloat Detection
Estimate index bloat and find indexes that need REINDEX CONCURRENTLY.
Maintenance+
-- Index sizes and usage SELECT schemaname, tablename, indexname, pg_size_pretty(pg_relation_size(indexrelid)) AS index_size, idx_scan, idx_tup_read, idx_tup_fetch FROM pg_stat_user_indexes JOIN pg_index USING (indexrelid) WHERE pg_relation_size(indexrelid) > 10485760 -- > 10MB ORDER BY pg_relation_size(indexrelid) DESC LIMIT 20; -- Rebuild a bloated index online (no lock) -- REINDEX INDEX CONCURRENTLY idx_name; -- Or rebuild all indexes on a table: -- REINDEX TABLE CONCURRENTLY mytable;
12 Β· Unused Indexes
Find indexes that are never (or rarely) used by queries β reducing write overhead and disk space.
Performance+
SELECT schemaname, tablename, indexname, idx_scan AS times_used, pg_size_pretty(pg_relation_size(indexrelid)) AS index_size, pg_size_pretty(pg_relation_size(indrelid)) AS table_size, CASE WHEN indisunique THEN 'UNIQUE' ELSE '' END AS unique_flag FROM pg_stat_user_indexes JOIN pg_index ON indexrelid = pg_index.indexrelid WHERE idx_scan = 0 AND NOT indisprimary AND NOT indisunique AND pg_relation_size(indexrelid) > 1048576 -- > 1MB ORDER BY pg_relation_size(indexrelid) DESC; -- DROP unused index (always test first): -- DROP INDEX CONCURRENTLY idx_name;
β Stats reset on server restart. Only act on indexes with zero scans over a long representative period (weeks). Never drop UNIQUE or PRIMARY KEY indexes.
13 Β· Tables with Many Sequential Scans (Missing Indexes)
Tables with high sequential scan counts relative to index scans β candidates for index creation.
Performance+
SELECT schemaname, relname AS table_name, seq_scan, idx_scan, round(seq_scan * 100.0 / NULLIF(seq_scan + idx_scan, 0), 1) AS seq_pct, seq_tup_read, pg_size_pretty(pg_relation_size(schemaname||'.'||relname)) AS table_size, n_live_tup FROM pg_stat_user_tables WHERE seq_scan > 100 AND pg_relation_size(schemaname||'.'||relname) > 10485760 -- > 10MB ORDER BY seq_tup_read DESC LIMIT 20;
π‘seq_pct > 90% on a large, frequently-queried table = likely missing index. Use EXPLAIN ANALYZE to confirm, then CREATE INDEX CONCURRENTLY to add without locking.
14 Β· Index vs Sequential Scan Ratio
Overall balance of index scans vs sequential scans across the entire database.
Performance+
SELECT SUM(seq_scan) AS total_seq_scans, SUM(idx_scan) AS total_idx_scans, round(SUM(idx_scan) * 100.0 / NULLIF(SUM(seq_scan) + SUM(idx_scan), 0), 1) AS idx_pct, SUM(seq_tup_read) AS rows_from_seq, SUM(idx_tup_fetch) AS rows_from_idx FROM pg_stat_user_tables;
π‘Target: idx_pct > 95% for OLTP workloads. Lower values indicate missing indexes or queries doing full-table scans.
15 Β· Index Usage Statistics
Scan counts and row fetch counts per index β identify over and under-utilized indexes.
Performance+
SELECT schemaname, tablename, indexname, idx_scan AS scan_count, idx_tup_read AS rows_read, idx_tup_fetch AS rows_fetched, pg_size_pretty(pg_relation_size(indexrelid)) AS index_size FROM pg_stat_user_indexes ORDER BY idx_scan DESC LIMIT 30;
16 Β· Table Statistics Health Check
Check if table statistics are stale β stale stats cause the planner to choose bad execution plans.
Performance+
SELECT schemaname, relname, n_live_tup, n_mod_since_analyze AS modified_since_analyze, round(n_mod_since_analyze * 100.0 / NULLIF(n_live_tup, 0), 1) AS mod_pct, last_autoanalyze, last_analyze FROM pg_stat_user_tables WHERE n_live_tup > 10000 ORDER BY n_mod_since_analyze DESC LIMIT 20; -- Force statistics update on a table -- ANALYZE VERBOSE mytable;
β mod_pct > 10% = statistics may be stale. Run ANALYZE. Stale statistics cause the planner to underestimate row counts and choose suboptimal plans.
Autovacuum
Status, tuning, workers, blockers
5 scripts
17 Β· Autovacuum Configuration Status
Show all autovacuum-related settings currently in effect.
Maintenance+
SELECT name, setting, unit, short_desc FROM pg_settings WHERE name LIKE 'autovacuum%' OR name LIKE 'vacuum%' ORDER BY name; -- Check per-table autovacuum overrides SELECT relname, reloptions FROM pg_class WHERE reloptions IS NOT NULL AND relkind = 'r' ORDER BY relname;
18 Β· Dead Tuple Report
Comprehensive dead tuple report with estimated autovacuum trigger thresholds per table.
Maintenance+
SELECT schemaname, relname, n_live_tup, n_dead_tup, round(n_dead_tup * 100.0 / NULLIF(n_live_tup + n_dead_tup,0), 1) AS dead_pct, -- autovacuum trigger threshold 50 + 0.2 * n_live_tup AS vacuum_threshold, n_dead_tup > (50 + 0.2 * n_live_tup) AS needs_vacuum, last_autovacuum, last_vacuum FROM pg_stat_user_tables WHERE n_live_tup > 100 ORDER BY n_dead_tup DESC LIMIT 25;
19 Β· Active Vacuum Workers
Show currently running autovacuum and manual VACUUM processes.
Maintenance+
SELECT pid, now() - query_start AS duration, query, state, wait_event_type, wait_event FROM pg_stat_activity WHERE query LIKE 'autovacuum%' OR query ILIKE '%vacuum%' ORDER BY query_start; -- Check autovacuum worker count vs max SELECT count(*) AS active_workers, current_setting('autovacuum_max_workers')::int AS max_workers FROM pg_stat_activity WHERE query LIKE 'autovacuum:%';
β If active_workers = max_workers constantly, autovacuum is overwhelmed. Increase autovacuum_max_workers or reduce autovacuum_vacuum_scale_factor to trigger more frequent but smaller vacuums.
20 Β· Last Vacuum / Analyze Timestamps
When was each table last vacuumed and analyzed β identify tables being neglected by autovacuum.
Maintenance+
SELECT schemaname, relname, last_vacuum, last_autovacuum, last_analyze, last_autoanalyze, vacuum_count, autovacuum_count, n_live_tup, n_dead_tup FROM pg_stat_user_tables ORDER BY GREATEST(last_vacuum, last_autovacuum) NULLS FIRST LIMIT 20;
π‘Tables with last_autovacuum = NULL or very old despite high n_dead_tup are not being vacuumed β check for long-running transactions blocking autovacuum (see script 21).
21 Β· Autovacuum Blockers (Idle-in-Transaction)
Find sessions whose open transactions are preventing autovacuum from advancing the XID horizon.
Maintenance+
-- Sessions blocking autovacuum's oldest xmin SELECT pid, usename, datname, state, backend_xmin, now() - xact_start AS open_transaction_age, left(query, 100) AS last_query FROM pg_stat_activity WHERE state IN ( 'idle in transaction', 'idle in transaction (aborted)' ) ORDER BY xact_start NULLS LAST; -- Overall oldest xmin in the cluster SELECT min(backend_xmin) AS oldest_xmin, now() - min(xact_start) AS oldest_txn_age FROM pg_stat_activity WHERE backend_xmin IS NOT NULL;
π¨Terminate long idle-in-transaction sessions:
SELECT pg_terminate_backend(pid); β Prevent recurrence: set idle_in_transaction_session_timeout = '10min' in postgresql.conf.Replication
Streaming replication status, lag, WAL, archiver, slots
6 scripts
22 Β· Replication Status (Primary)
Full replication status from primary β connected standbys, sync state, and sent/replayed LSN positions.
HA+
SELECT application_name, client_addr, state, sync_state, sent_lsn, write_lsn, flush_lsn, replay_lsn, write_lag, flush_lag, replay_lag, pg_size_pretty(sent_lsn - replay_lsn) AS lag_bytes FROM pg_stat_replication ORDER BY application_name;
π‘sync_state values:
async (default), sync (synchronous), potential (will become sync if current sync fails), quorum.23 Β· Replication Lag Monitor
Lag in bytes and seconds β run on both primary (to see all standbys) and on each standby.
HA+
-- On PRIMARY: lag per standby SELECT application_name, pg_size_pretty(pg_wal_lsn_diff( pg_current_wal_lsn(), replay_lsn)) AS replay_lag_bytes, replay_lag AS replay_lag_time, flush_lag, write_lag FROM pg_stat_replication; -- On STANDBY: lag from this replica's perspective SELECT now() - pg_last_xact_replay_timestamp() AS replication_delay, pg_last_wal_receive_lsn() AS received_lsn, pg_last_wal_replay_lsn() AS replayed_lsn, pg_is_in_recovery() AS is_standby;
β Alert thresholds: <1s = good, 1β30s = acceptable, >60s = investigate. High lag = standby I/O bottleneck, network issue, or standby under heavy query load.
24 Β· Replication Slot Monitor
All replication slots with WAL retained and active status. Critical to prevent pg_wal directory filling up.
HA+
SELECT slot_name, slot_type, database, active, active_pid, pg_size_pretty(pg_wal_lsn_diff( pg_current_wal_lsn(), COALESCE(confirmed_flush_lsn, restart_lsn))) AS wal_retained, restart_lsn, confirmed_flush_lsn FROM pg_replication_slots ORDER BY pg_wal_lsn_diff( pg_current_wal_lsn(), COALESCE(confirmed_flush_lsn, restart_lsn)) DESC; -- Drop a stale/unused slot (DANGEROUS if consumer still needs it) -- SELECT pg_drop_replication_slot('slot_name');
π¨An inactive slot retaining >10GB of WAL is a disk-filling risk. Alert when wal_retained > 5GB. Set max_slot_wal_keep_size = 10GB in postgresql.conf as a safety valve (PG 13+).
25 Β· WAL Generation Statistics
WAL write volume, sync calls, and checkpoint statistics for write amplification analysis.
HA+
-- WAL generation stats (PostgreSQL 14+) SELECT wal_records, wal_fpi AS full_page_images, pg_size_pretty(wal_bytes) AS wal_bytes, wal_buffers_full, wal_write, wal_sync, round(wal_write_time / 1000, 2) AS write_time_sec, round(wal_sync_time / 1000, 2) AS sync_time_sec FROM pg_stat_wal; -- Checkpoint stats SELECT checkpoints_timed, checkpoints_req, buffers_checkpoint, buffers_clean, buffers_backend, round(checkpoint_write_time / 1000, 2) AS write_sec, round(checkpoint_sync_time / 1000, 2) AS sync_sec FROM pg_stat_bgwriter;
26 Β· WAL Archiver Status
Check if WAL archiving is working correctly β failed archives break PITR capability.
HA+
SELECT archived_count, last_archived_wal, last_archived_time, failed_count, last_failed_wal, last_failed_time, now() - last_archived_time AS time_since_last_archive FROM pg_stat_archiver; -- Verify archive_mode and archive_command SHOW archive_mode; SHOW archive_command; -- Force a WAL switch to test archive_command -- SELECT pg_switch_wal();
π¨If failed_count > 0 or time_since_last_archive > 10 minutes, your PITR backup is broken. Check archive_command permissions and disk space immediately.
27 Β· Recovery / Standby Status
Check if this server is in recovery (standby) or is a primary, and current LSN positions.
HA+
SELECT pg_is_in_recovery() AS is_standby, pg_last_wal_receive_lsn() AS received_lsn, pg_last_wal_replay_lsn() AS replayed_lsn, pg_last_xact_replay_timestamp() AS last_replay_time, now() - pg_last_xact_replay_timestamp() AS replay_lag; -- Promote standby to primary (PostgreSQL 12+) -- SELECT pg_promote(); -- Or: pg_ctl promote -D $PGDATA
Backup
pg_basebackup, pg_dump, PITR setup, verification
4 scripts
28 Β· pg_basebackup Commands
Ready-to-use pg_basebackup commands for various scenarios β backup to local dir, with WAL, background mode.
Backup+
# Standard backup (tar format, stream WAL, show progress) pg_basebackup \ -U postgres \ -D /pgBackup/pgsql17/backup/base_$(date +%d%b%Y) \ -Ft -Xs -P -v \ > /pgBackup/pgsql17/backup/base_$(date +%d%b%Y).log 2>&1 # Run in background with nohup nohup pg_basebackup \ -U postgres \ -D /pgBackup/pgsql17/backup/base_$(date +%d%b%Y) \ -Ft -Xs -P -v \ > /pgBackup/pgsql17/backup/base_$(date +%d%b%Y).log 2>&1 & # Remote backup (from standby or DBA host) pg_basebackup \ -h 192.168.1.51 -p 5432 \ -U repl_user \ -D /pgBackup/base_$(date +%d%b%Y) \ -Ft -Xs -P -v -R # -R flag auto-creates standby.signal + postgresql.auto.conf # for streaming replication setup
π‘Flags: -Ft = tar format, -Xs = stream WAL during backup, -P = progress, -v = verbose, -R = write recovery config (for standby setup).
29 Β· pg_dump / pg_restore Commands
Complete pg_dump and pg_restore command reference for database, schema, and table-level operations.
Backup+
# Dump entire database (custom format β recommended) pg_dump -U postgres -d mydb -Fc -v \ -f /backup/mydb_$(date +%d%b%Y).dmp # Dump schema only (no data) pg_dump -U postgres -d mydb --schema-only \ -f /backup/mydb_schema.sql # Dump data only pg_dump -U postgres -d mydb --data-only \ -Fc -f /backup/mydb_data.dmp # Dump specific table pg_dump -U postgres -d mydb \ -t public.orders -Fc -v \ -f /backup/orders_$(date +%d%b%Y).dmp # Dump all databases + global objects pg_dumpall -U postgres -f /backup/all_$(date +%d%b%Y).sql # Restore full database pg_restore -U postgres -d mydb -v \ /backup/mydb_$(date +%d%b%Y).dmp # Restore single table into existing DB pg_restore -U postgres -d mydb \ -t public.orders -v /backup/orders.dmp # Restore with parallel workers (faster) pg_restore -U postgres -d mydb -j 4 -v \ /backup/mydb.dmp
30 Β· PITR Configuration Checklist
Step-by-step postgresql.conf settings and signal file creation for Point-in-Time Recovery.
Backup+
# 1. On SOURCE β enable archiving (postgresql.conf) wal_level = replica archive_mode = on archive_command = 'cp %p /pgArch/pgsql17/arch/%f' # 2. On TARGET β restore base backup then configure recovery # Edit postgresql.conf on target: restore_command = 'cp /pgbackup/archive/%f %p' recovery_target_time = '2025-10-12 22:05:00' recovery_target_action = 'pause' # or 'promote' # 3. Create recovery.signal (PostgreSQL 12+) touch $PGDATA/recovery.signal chmod 600 $PGDATA/recovery.signal # 4. Start PostgreSQL β it will enter recovery mode systemctl start postgresql-17 # 5. Monitor recovery in logs tail -f $PGDATA/log/postgresql-*.log # 6. After pausing at target time, validate data, then promote
-- Check if paused at target time SELECT pg_is_in_recovery(), pg_last_xact_replay_timestamp(); -- Validate your data is as expected SELECT * FROM myschema.mytable LIMIT 10; -- Promote to full primary SELECT pg_wal_replay_resume(); -- Verify promotion complete SELECT pg_is_in_recovery(); -- should return 'f'
31 Β· Backup Verification
Verify integrity of a pg_basebackup using pg_verifybackup and check backup_manifest.
Backup+
# Verify backup integrity using backup_manifest pg_verifybackup /pgBackup/pgsql17/backup/base_12OCT2025 # Skip WAL verification (if WAL is not available) pg_verifybackup --no-parse-wal \ /pgBackup/pgsql17/backup/base_12OCT2025 # Check backup_manifest exists and is readable ls -la /pgBackup/pgsql17/backup/base_12OCT2025/backup_manifest cat /pgBackup/pgsql17/backup/base_12OCT2025/backup_manifest | head -5 # Disk usage of backup du -sh /pgBackup/pgsql17/backup/base_12OCT2025
π‘Always verify backups. An untested backup is not a backup. Schedule monthly restore drills in a test environment.
Administration
Users, locks, partitions, config, housekeeping
8 scripts
32 Β· User & Role Management
Common user and role management commands β create, grant, revoke, list, and drop roles.
Admin+
-- List all roles and their attributes \du -- Or detailed: SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolreplication, rolconnlimit, rolvaliduntil FROM pg_roles ORDER BY rolname; -- Create application user CREATE ROLE app_user WITH LOGIN PASSWORD 'StrongP@ss123' CONNECTION LIMIT 50 VALID UNTIL '2026-12-31'; -- Grant database and schema access GRANT CONNECT ON DATABASE mydb TO app_user; GRANT USAGE ON SCHEMA public TO app_user; GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user; GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_user; -- Create read-only role CREATE ROLE readonly_role; GRANT CONNECT ON DATABASE mydb TO readonly_role; GRANT USAGE ON SCHEMA public TO readonly_role; GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_role; -- Change password ALTER ROLE app_user WITH PASSWORD 'NewP@ss456'; -- Drop user (must own no objects) REASSIGN OWNED BY app_user TO postgres; DROP OWNED BY app_user; DROP ROLE app_user;
33 Β· Tablespace Information
List tablespaces, their locations, sizes, and which databases use them.
Admin+
-- List all tablespaces with size SELECT spcname AS tablespace_name, pg_catalog.pg_tablespace_location(oid) AS location, pg_catalog.pg_get_userbyid(spcowner) AS owner, pg_size_pretty(pg_tablespace_size(spcname)) AS size FROM pg_tablespace ORDER BY spcname; -- Which databases use each tablespace? SELECT t.spcname AS tablespace, d.datname AS database FROM pg_database d JOIN pg_tablespace t ON d.dattablespace = t.oid ORDER BY t.spcname; -- Symbolic links in pg_tblspc directory -- ls -la $PGDATA/pg_tblspc/
34 Β· Lock Monitor
Active locks with relation names, modes, and whether they are granted or waiting.
Monitor+
SELECT pid, locktype, COALESCE(relation::regclass::text, 'β') AS relation, mode, granted, fastpath, pg_stat_activity.state, pg_stat_activity.wait_event_type, pg_stat_activity.wait_event, left(pg_stat_activity.query, 60) AS query FROM pg_locks JOIN pg_stat_activity USING (pid) WHERE locktype != 'virtualxid' AND NOT fastpath ORDER BY granted, pid;
π‘
granted = false means the lock is waiting (blocked). Match with script 04 (Blocking Sessions) to find the root cause.35 Β· Partition Information
List all partition tables, their partition keys, partition count, and sizes.
Admin+
-- List all partitioned tables and their partition count SELECT parent.relname AS parent_table, parent.relkind, COUNT(child.relname) AS partition_count, pg_size_pretty(SUM(pg_relation_size(child.oid))) AS total_partitions_size FROM pg_class parent JOIN pg_inherits ON pg_inherits.inhparent = parent.oid JOIN pg_class child ON pg_inherits.inhrelid = child.oid WHERE parent.relkind = 'p' GROUP BY parent.relname, parent.relkind ORDER BY parent.relname; -- List partitions of a specific table SELECT child.relname AS partition_name, pg_get_expr(child.relpartbound, child.oid) AS partition_bound, pg_size_pretty(pg_relation_size(child.oid)) AS size FROM pg_class parent JOIN pg_inherits ON pg_inherits.inhparent = parent.oid JOIN pg_class child ON pg_inherits.inhrelid = child.oid WHERE parent.relname = 'orders' -- change table name ORDER BY child.relname;
36 Β· Configuration Health Check
Show key performance parameters and compare current values against recommended best practices.
Admin+
-- Key performance parameters SELECT name, setting, unit, source FROM pg_settings WHERE name IN ( 'shared_buffers', 'work_mem', 'maintenance_work_mem', 'effective_cache_size', 'max_connections', 'wal_level', 'archive_mode', 'checkpoint_timeout', 'max_wal_size', 'checkpoint_completion_target', 'synchronous_commit', 'autovacuum_max_workers', 'autovacuum_vacuum_scale_factor', 'autovacuum_vacuum_cost_limit', 'random_page_cost', 'jit', 'wal_compression', 'max_parallel_workers' ) ORDER BY name; -- Show parameters that differ from defaults SELECT name, setting, boot_val AS default_val, source FROM pg_settings WHERE setting != boot_val AND source != 'default' ORDER BY name;
37 Β· Kill / Cleanup Idle Sessions
Scripts to cancel or terminate sessions by state, age, or user β use carefully in production.
Maintenance+
-- Cancel (soft) all queries running > 10 minutes SELECT pg_cancel_backend(pid) FROM pg_stat_activity WHERE state = 'active' AND now() - query_start > interval '10 minutes' AND pid != pg_backend_pid(); -- Terminate idle-in-transaction sessions older than 30 min SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle in transaction' AND now() - xact_start > interval '30 minutes' AND pid != pg_backend_pid(); -- Terminate all idle connections (not running anything) SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND now() - state_change > interval '1 hour' AND usename != 'replication' AND pid != pg_backend_pid();
π¨Always preview with SELECT pid, state, query... first before executing the terminate. pg_cancel_backend is gentler (sends SIGINT). pg_terminate_backend sends SIGTERM and disconnects the client.
38 Β· Object Sizes by Schema
Space usage breakdown by schema β tables, indexes, total. Useful for capacity planning.
Admin+
SELECT schemaname, COUNT(tablename) AS table_count, pg_size_pretty(SUM(pg_relation_size(schemaname||'.'||tablename))) AS tables_size, pg_size_pretty(SUM(pg_indexes_size(schemaname||'.'||tablename))) AS indexes_size, pg_size_pretty(SUM(pg_total_relation_size(schemaname||'.'||tablename))) AS total_size FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema') GROUP BY schemaname ORDER BY SUM(pg_total_relation_size(schemaname||'.'||tablename)) DESC;
39 Β· Extensions & PostgreSQL Version
Installed extensions with versions, and full PostgreSQL version information.
Admin+
-- PostgreSQL version SELECT version(); SHOW server_version; -- Installed extensions SELECT extname AS extension, extversion AS version, nspname AS schema, extrelocatable AS relocatable FROM pg_extension JOIN pg_namespace ON extnamespace = pg_namespace.oid ORDER BY extname; -- Available extensions (can be installed) SELECT name, default_version, comment FROM pg_available_extensions WHERE installed_version IS NULL ORDER BY name; -- Cluster uptime SELECT pg_postmaster_start_time() AS started_at, now() - pg_postmaster_start_time() AS uptime;
Rajasekhar Amudala
Senior Oracle DBA | 16+ Years | RAC Β· Exadata Β· PostgreSQL Β· Performance Tuning
β CAUTION: Scripts provided for educational use. Test in your environment before using in production. Use at your own risk.