Part 10 · 3 chapters · ~18 min

Operating Postgres

The configuration knobs that matter with reasons, the pg_stat views and what each tells you, lock modes, pg_locks and lock queues, the ACCESS EXCLUSIVE trap, safe schema migrations by lock taken, backups with pg_dump, pg_basebackup, pgBackRest and WAL-G, and upgrades with pg_upgrade or logical replication.

26

Configuration and the stat views

settingstarting pointreason
shared_buffers25% of RAMPostgres' own cache; the OS cache does the rest
effective_cache_size50-75% of RAMplanner hint for index costing
work_mem16-64 MB, per sort/hash nodeavoid disk spills without OOM across many backends
maintenance_work_mem512 MB - 2 GBfaster vacuum and index builds
max_wal_size, checkpoint_timeoutseveral GB, 15 minfewer checkpoints and full page writes
random_page_cost1.1 on SSDthe planner should like indexes on fast storage
autovacuum_*lower scale factors on big tables, higher cost limitsvacuum keeps up
idle_in_transaction_session_timeout, statement_timeoutset themprotect vacuum and locks from forgotten sessions
viewtells you
pg_stat_activitywho is running what, waiting on what
pg_stat_user_tables / _indexesscans, tuples, dead tuples, vacuum times, index use
pg_statio_*buffer hits versus reads per table and index
pg_stat_statementsthe workload, normalised
pg_stat_replication, pg_replication_slotsreplica lag and retained WAL
pg_stat_io (16+)IO by backend type and context
27

Locks and safe migrations

code
-- who blocks whom
SELECT blocked.pid, blocked.query, blocking.pid AS blocking_pid, blocking.query AS blocking_query
FROM pg_stat_activity blocked
JOIN LATERAL unnest(pg_blocking_pids(blocked.pid)) b(pid) ON true
JOIN pg_stat_activity blocking ON blocking.pid = b.pid;
operationlock / costsafe pattern
ADD COLUMN (nullable or with a constant default)ACCESS EXCLUSIVE, metadata only (11+)lock_timeout + retry
ADD COLUMN with a volatile defaultrewrites the tableadd nullable, backfill in batches, then set default
CREATE INDEXSHARE: blocks writesCREATE INDEX CONCURRENTLY
ADD FOREIGN KEY / CHECKvalidates every row under lockADD ... NOT VALID, then VALIDATE CONSTRAINT
SET NOT NULLfull scan under lockadd a NOT VALID CHECK (col IS NOT NULL), validate, then SET NOT NULL (uses it)
ALTER COLUMN TYPEusually a rewritenew column, dual write, backfill, switch
THE ACCESS EXCLUSIVE TRAP
a harmless ALTER waits behind a long query, and everything else queues behind the ALTER
blocksblockslong SELECTACCESS SHARE, 4 minALTER TABLE ADD COLUMNwants ACCESS EXCLUSIVEevery new queryACCESS SHARE, queued
swipe the figure sideways, or tap expand for full screen
1/4
the long reader
An analytics query has held ACCESS SHARE on the table for four minutes. That is fine: readers do not block readers or writers.
a long query holds a weak lockharmless on its own
28

Backups and upgrades

toolkinduse
pg_dumplogical (SQL or custom format), per databasesmall databases, migrations, partial copies; slow restore at scale
pg_basebackupphysical copy of the clusterseeding standbys; base for PITR
pgBackRestphysical, incremental and differential, parallel, verified, with WAL archivingproduction backup and PITR
WAL-Gphysical with WAL archiving to object storage, delta backupscloud-native PITR

Upgrades: pg_upgrade --link rewrites the catalogue for the new major and hard-links data files: minutes of downtime regardless of size, then ANALYZE (statistics are not carried over before 18). For near-zero downtime, build the new version as a logical replica, let it catch up, then switch writes over. Always test the restore, and time it: restore time is the real RTO.