CtrlK
BlogDocsLog inGet started
Tessl Logo

alonso-skills/postgres

Comprehensive PostgreSQL reference for developers and DBAs covering versions 14–18. Use whenever the user asks about PostgreSQL syntax, DDL/DML/DQL, joins, LATERAL, CTEs, window functions, GROUPING SETS, DISTINCT ON, RETURNING, ON CONFLICT, PL/pgSQL, functions, procedures, triggers, views, materialized views, indexes (B-tree/GIN/GiST/BRIN/Hash/Bloom), MVCC, VACUUM, autovacuum, WAL, TOAST, partitioning, replication (streaming/logical), backup, PITR, HA (Patroni/repmgr), pgBouncer, EXPLAIN ANALYZE, RLS, roles, extensions (pgvector, PostGIS, TimescaleDB, Citus, pg_trgm, pg_cron), JSON/JSONB, full-text search, UUID, timestamptz, COPY, system catalogs, collations, large objects, cursors, GUC, or any Postgres administration, performance, security, replication, backup, or recovery topic.

94

1.36x
Quality

94%

Does it follow best practices?

Impact

94%

1.36x

Average score across 3 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

58-performance-diagnostics.mdreferences/

Performance Diagnostics — pg_stat_* Views and Live Inspection

A field guide to PostgreSQL's cumulative statistics system, the per-view catalog, the wait-event taxonomy, and the runnable recipes that diagnose almost every production incident: long-running queries, blocked sessions, bloat, replication lag, I/O bottlenecks, and stalled maintenance operations.

Table of Contents

When to Use This Reference

Reach for this file when an operator needs to look at a running cluster and answer "what is happening right now?" or "what has been happening since the last stats reset?" The pg_stat_* catalog is the canonical live-introspection surface. Every other diagnostic file in this skill — locking (43-locking.md), vacuum (28-vacuum-autovacuum.md), buffer manager (32-buffer-manager.md), WAL (33-wal.md), checkpoints (34-checkpoints-bgwriter.md), pg_stat_statements (57-pg-stat-statements.md) — references one or more views described here.

This file is the picker for which view answers which question. The per-view deep dives live in the related topic files; this is the cross-cutting routing layer.

The Five-Rule Mental Model

  1. pg_stat_activity is the "what's happening right now?" view. One row per backend (client connection, autovacuum worker, replication walsender, parallel worker, logical-replication apply worker). Filter by state, group by wait_event_type/wait_event, join to pg_blocking_pids() to find blocking chains. Every diagnostic walk starts here.

  2. wait_event_type and wait_event route every "why is this slow?" investigation. A backend in state = 'active' with wait_event_type IS NULL is on CPU. With wait_event_type = 'Lock' it is blocked on a heavyweight lock (route to 43-locking.md). With wait_event_type = 'IO' it is waiting on disk (route to 32-buffer-manager.md and 33-wal.md). With wait_event_type = 'LWLock' it is waiting on an internal lightweight lock (route to the specific lock name).

  3. Cumulative views accumulate since the last reset, not since cluster start. pg_stat_database, pg_stat_user_tables, pg_stat_user_indexes, pg_statio_*, pg_stat_bgwriter, pg_stat_checkpointer, pg_stat_io, pg_stat_wal — all carry stats_reset columns or rely on pg_stat_reset_*(). A rate calculation requires two snapshots; absolute values without a baseline are nearly useless on a long-running cluster.

  4. pg_stat_progress_* views show in-flight maintenance operations. Six progress views as of PG16: vacuum, analyze, create_index, basebackup, copy, cluster. Each has one row per running operation with phase plus operation-specific progress columns. pg_stat_progress_vacuum does NOT show VACUUM FULL — that uses pg_stat_progress_cluster (because VACUUM FULL is implemented as a CLUSTER-style table rewrite).

  5. The view-catalog has version-introduced columns and version-renamed columns. PG14 added pg_stat_wal + pg_stat_replication_slots + pg_locks.waitstart + session columns on pg_stat_database. PG15 added pg_stat_subscription_stats. PG16 added pg_stat_io + last_seq_scan/last_idx_scan columns + n_tup_newpage_upd. PG17 split pg_stat_checkpointer out of pg_stat_bgwriter (and removed buffers_backend/buffers_backend_fsync), renamed pg_stat_progress_vacuum.max_dead_tuples to max_dead_tuple_bytes, and renamed pg_stat_statements.blk_*_time to shared_blk_*_time. PG18 added bytes columns to pg_stat_io (and removed op_bytes), added WAL rows to pg_stat_io, added pg_stat_get_backend_io(), added parallel_workers_to_launch/parallel_workers_launched to pg_stat_database, added num_done and slru_written to pg_stat_checkpointer, and added the pg_aios async-I/O view.

[!WARNING] Monitoring queries written for PG16 break on PG17 and PG18 The PG17 pg_stat_checkpointer split, the PG17 pg_stat_progress_vacuum column renames, and the PG18 pg_stat_io.op_bytes removal all silently produce zero-row or null-column results when an unmodified PG16-era query runs against the newer cluster. Audit your monitoring after any major-version upgrade. See Gotcha #6 and the recipes for side-by-side rewrites.

Decision Matrix

You want to find / measureUse this viewFilter / joinAvoid
Currently-running queriespg_stat_activityWHERE state = 'active' AND backend_type = 'client backend' ORDER BY query_startfiltering on state_change (it advances even for idle backends)
Blocked sessions and their blockerspg_stat_activity + pg_blocking_pids()LATERAL unnest(pg_blocking_pids(pid))guessing from log messages
Long-running transactionspg_stat_activityWHERE xact_start IS NOT NULL AND xact_start < now() - interval '5 min'filtering on query_start (a tx may be idle-in-transaction with no current query)
Idle-in-transaction sessionspg_stat_activityWHERE state = 'idle in transaction' AND state_change < now() - interval '30s'killing without checking pid for autovacuum or walsender
Cache hit ratio per databasepg_stat_databaseblks_hit::float / NULLIF(blks_hit + blks_read, 0)reading absolute values without stats_reset baseline
Tables needing autovacuum attentionpg_stat_user_tablesWHERE n_dead_tup > 10000 ORDER BY n_dead_tup::float / NULLIF(n_live_tup, 0) DESChard cutoffs without considering table size
Unused indexespg_stat_user_indexesWHERE idx_scan = 0 AND NOT pg_index.indisuniquedropping without checking replica index usage
Table/index buffer hit ratepg_statio_user_tables / pg_statio_user_indexesheap_blks_hit::float / NULLIF(heap_blks_hit + heap_blks_read, 0)conflating "hit rate" with "performance"
Per-relation I/O attribution (PG16+)pg_stat_ioGROUP BY backend_type, contextpre-PG16 (use pg_stat_bgwriter + pg_statio_*)
WAL volume rate (PG14+)pg_stat_waltwo snapshots over timereading without time delta
Long-running VACUUMpg_stat_progress_vacuum JOIN pg_stat_activityWHERE wait_event_type IS NOT NULL flags blockingconfusing with VACUUM FULL (use pg_stat_progress_cluster)
Long-running CREATE INDEX (CONCURRENTLY)pg_stat_progress_create_indexWHERE phase = 'waiting for writers before build' flags long-running-tx blockerreading without xact_start join
Replication lag per standbypg_stat_replication on primarypg_wal_lsn_diff(sent_lsn, replay_lsn) and the time-lag columnsreading replay_lag = NULL as "no lag" (it's NULL on idle replicas)
Slot WAL retentionpg_replication_slotspg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)ignoring abandoned slots that pin disk
Subscriber-side conflicts (PG15+)pg_stat_subscription_statsWHERE apply_error_count > 0 OR sync_error_count > 0pre-PG15 (no view; read from logs)

Smell signals.

  • pg_stat_activity showing many rows with state = 'idle in transaction' and state_change more than 30 seconds in the past → applications leaking transactions; set idle_in_transaction_session_timeout (cross-reference 41-transactions.md).
  • pg_stat_database.deadlocks climbing → applications are not ordering locks consistently; route to 43-locking.md Recipe 8.
  • pg_stat_user_tables.n_dead_tup rising but last_autovacuum IS NULL (or very old) for hours → autovacuum is being canceled by lock conflicts, or the xmin horizon is held back by a long transaction; route to 27-mvcc-internals.md Recipe 2.

Syntax / Mechanics

The view-catalog overview

PostgreSQL exposes its statistics through a family of system views. The canonical inventory:1

CategoryViews (PG16 baseline)
Activitypg_stat_activity, pg_stat_ssl, pg_stat_gssapi
Database-levelpg_stat_database, pg_stat_database_conflicts
Per-tablepg_stat_all_tables, pg_stat_user_tables, pg_stat_sys_tables, pg_stat_xact_*_tables
Per-indexpg_stat_all_indexes, pg_stat_user_indexes, pg_stat_sys_indexes
Per-functionpg_stat_user_functions, pg_stat_xact_user_functions
I/O per-relationpg_statio_all_tables, pg_statio_user_tables, pg_statio_sys_tables, pg_statio_all_indexes, pg_statio_user_indexes, pg_statio_sys_indexes, pg_statio_all_sequences, pg_statio_user_sequences, pg_statio_sys_sequences
I/O cluster-wide (PG16+)pg_stat_io
Background processespg_stat_bgwriter, pg_stat_checkpointer (PG17+), pg_stat_archiver, pg_stat_wal (PG14+), pg_stat_slru
Replicationpg_stat_replication, pg_replication_slots, pg_stat_replication_slots (PG14+), pg_stat_wal_receiver, pg_stat_subscription, pg_stat_subscription_stats (PG15+)
Progresspg_stat_progress_vacuum, pg_stat_progress_analyze, pg_stat_progress_create_index, pg_stat_progress_basebackup, pg_stat_progress_copy (PG14+), pg_stat_progress_cluster
Extension-providedpg_stat_statements (cross-reference 57-pg-stat-statements.md)
PG18+pg_aios (async I/O)

pg_stat_activity

Verbatim docs definition:2 "The pg_stat_activity view will have one row per server process, showing information related to the current activity of that process."

Key columns (PG16):

ColumnTypeMeaning
datid / datnameoid / nameDatabase OID and name
pidintegerProcess ID
leader_pidintegerIf part of a parallel group, PID of the leader; otherwise NULL
usesysid / usenameoid / nameConnected role OID and name
application_nametextClient-set application_name GUC
client_addr / client_hostname / client_portinet / text / integerNetwork identification of the client
backend_starttimestamptzWhen this backend started
xact_starttimestamptzWhen the current transaction started, or NULL if not in a transaction
query_starttimestamptzWhen the current query started, or for idle/idle in transaction when the previous query ended
state_changetimestamptzWhen the state last changed
wait_event_typetextType of event the backend is waiting on (NULL = not waiting)
wait_eventtextSpecific event name
statetextOne of active, idle, idle in transaction, idle in transaction (aborted), fastpath function call, disabled
backend_xidxidTop-level transaction ID, or NULL if no XID assigned yet
backend_xminxidBackend's xmin horizon (cross-reference 27-mvcc-internals.md)
query_idbigintQuery identifier (PG14+, if compute_query_id = on or auto)
querytextCurrent query text (or last query if idle) — truncated at track_activity_query_size
backend_typetextOne of autovacuum launcher, autovacuum worker, logical replication launcher, logical replication worker, parallel worker, background writer, client backend, checkpointer, archiver, startup, walreceiver, walsender, walwriter, plus extension-defined types

The verbatim wait-event-vs-state independence rule:1 "The wait_event and state columns are independent. If a backend is in the active state, it may or may not be waiting on some event. If the state is active and wait_event is non-null, it means that a query is being executed, but is being blocked somewhere in the system."

Wait events

Wait events are categorized by wait_event_type into nine top-level classes:1

TypeWhat it indicates
ActivityServer process is idle. This is the wait-event type used by background processes (e.g., the WAL writer, the checkpointer) waiting for activity in their main processing loop. Generally a good state — these are not blocking anything.
BufferPinWaiting to acquire an exclusive pin on a buffer. Buffer-pin waits can occur if another process is reading data into the buffer or if VACUUM is waiting to clean a buffer.
ClientWaiting for activity from a client process (e.g., reading from or writing to a socket).
ExtensionWaiting in an extension. Use the specific wait_event to disambiguate.
IOWaiting for an I/O operation. Common wait_event values: DataFileRead, DataFileWrite, DataFileFlush, WALRead, WALWrite, WALSync, BufFileRead, BufFileWrite, RelationMapRead.
IPCWaiting for inter-process communication (parallel-query messaging, replication, sinval).
LockVerbatim: "The server process is waiting for a heavyweight lock. Heavyweight locks, also known as lock manager locks or simply locks, primarily protect SQL-visible objects such as tables." The specific wait_event names the lock type (relation, tuple, transactionid, virtualxid, extend, page, frozenid, object, userlock, advisory, applytransaction PG16+). Cross-reference 43-locking.md.
LWLockVerbatim: "The server process is waiting for a lightweight lock. Most such locks protect a particular data structure in shared memory." The specific wait_event names the LWLock (WALWrite, LockManager, BufferContent, XidGen, ProcArray, etc.).
TimeoutWaiting for a timeout to expire (statement_timeout, lock_timeout, idle_in_transaction_session_timeout, recovery_apply_delay).

[!NOTE] PostgreSQL 17 "Add system view pg_wait_events that reports wait event types (Bertrand Drouvot). This is useful for adding descriptions to wait events reported in pg_stat_activity."3 Join to it for human-readable descriptions of the cryptic wait_event names.

pg_stat_database

One row per database in the cluster. Key columns:1

ColumnTypeMeaning
datid / datnameoid / nameDatabase identification
numbackendsintegerCurrent backend count
xact_commit / xact_rollbackbigintLifetime transaction counts
blks_read / blks_hitbigintBlock reads (from disk or OS cache) and hits (in shared_buffers)
tup_returned / tup_fetchedbigintTuples returned by queries / actually fetched (returned > fetched on aggregate-heavy workloads)
tup_inserted / tup_updated / tup_deletedbigintDML row counts
conflictsbigintRecovery conflicts canceling queries on standbys
temp_files / temp_bytesbigintTemp files created (sorts/hashes spilling to disk)
deadlocksbigintDetected deadlocks
checksum_failuresbigintData-page checksum failures (cross-reference 88-corruption-recovery.md)
blk_read_time / blk_write_timedouble precisionI/O time, requires track_io_timing = on
session_timedouble precisionTotal session wall time (PG14+)
active_time / idle_in_transaction_timedouble precisionTime spent in active vs idle-in-tx states (PG14+)
sessionsbigintTotal established sessions (PG14+)
sessions_abandoned / sessions_fatal / sessions_killedbigintSession termination causes (PG14+)
parallel_workers_to_launch / parallel_workers_launchedbigintPG18+: planned vs actually-launched parallel workers (delta = pool saturation)
stats_resettimestamptzWhen stats were last reset

[!NOTE] PostgreSQL 14 "Add session statistics to the pg_stat_database system view (Laurenz Albe)."4 The session_time, active_time, idle_in_transaction_time, sessions, sessions_abandoned, sessions_fatal, sessions_killed columns were added in PG14.

Table and index statistics

Per-table: pg_stat_all_tables (and the _user_tables / _sys_tables filtered variants). One row per table.

Key columns:

ColumnTypeMeaning
relid / schemaname / relnameoid / name / nameIdentification
seq_scan / seq_tup_readbigintSequential scans and rows returned
idx_scan / idx_tup_fetchbigintIndex scans and rows fetched via index
n_tup_ins / n_tup_upd / n_tup_delbigintRow insertion/update/deletion counts
n_tup_hot_updbigintHOT updates (cross-reference 30-hot-updates.md)
n_tup_newpage_updbigintPG16+: updates that moved the row to a new page
n_live_tup / n_dead_tupbigintApproximate live/dead tuple count
n_mod_since_analyze / n_ins_since_vacuumbigintMod/insert counts since last analyze/vacuum (drive autovacuum trigger)
last_vacuum / last_autovacuum / last_analyze / last_autoanalyzetimestamptzLast operation timestamps (manual vs autovacuum-initiated)
vacuum_count / autovacuum_count / analyze_count / autoanalyze_countbigintLifetime counts
last_seq_scan / last_idx_scantimestamptzPG16+: most recent scan timestamps (use to find truly-unused indexes vs not-recently-scanned)

Per-index: pg_stat_all_indexes. One row per index.

ColumnMeaning
idx_scanNumber of index scans initiated. Zero across a representative time window is the canonical "unused index" signal.
idx_tup_readIndex entries returned by scans
idx_tup_fetchHeap rows fetched by simple index scans (zero for index-only scans without heap fetches)
last_idx_scanPG16+: timestamp of most recent scan

Per-relation I/O: pg_statio_user_tables / pg_statio_user_indexes. Block-level hit/read counters.

ColumnMeaning
heap_blks_read / heap_blks_hitHeap block reads / hits
idx_blks_read / idx_blks_hitIndex block reads / hits (per-table aggregate)
toast_blks_read / toast_blks_hitTOAST table block reads / hits (cross-reference 31-toast.md)
tidx_blks_read / tidx_blks_hitTOAST index block reads / hits

pg_stat_io (PG16+)

[!NOTE] PostgreSQL 16 "Add system view pg_stat_io view to track I/O statistics (Melanie Plageman)."5 This is the modern replacement for pg_stat_bgwriter.buffers_backend (which was removed in PG17 per the cross-reference below) and the right place to attribute I/O to backend types and contexts.

pg_stat_io decomposes I/O by (backend_type, context, object):

Column (PG16)Meaning
backend_typeclient backend, autovacuum worker, background writer, checkpointer, standalone backend, startup, walsender, bgworker, wal writer
contextnormal, vacuum, bulkread, bulkwrite (ring-buffer contexts; cross-reference 32-buffer-manager.md)
objectrelation or temp relation
reads / writes / writebacks / extends / hits / evictions / reuses / fsyncsCounters per I/O operation
read_time / write_time / writeback_time / extend_time / fsync_timeAggregate time (requires track_io_timing = on)
op_bytesPG16-17 only: bytes per I/O operation (always BLCKSZ = 8192). Removed in PG18.
read_bytes / write_bytes / extend_bytesPG18+ replacement for op_bytes-derived volume computations

[!NOTE] PostgreSQL 18 "Add pg_stat_io columns to report I/O activity in bytes (Nazir Bilal Yavuz). The new columns are read_bytes, write_bytes, and extend_bytes. The op_bytes column, which always equalled BLCKSZ, has been removed."6 Also: "Add WAL I/O activity rows to pg_stat_io."7 And per-backend variant via pg_stat_get_backend_io():8 "Add per-backend I/O statistics reporting (Bertrand Drouvot). The statistics are accessed via pg_stat_get_backend_io(). Per-backend I/O statistics can be cleared via pg_stat_reset_backend_stats()."

pg_stat_wal (PG14+)

[!NOTE] PostgreSQL 14 "Add system view pg_stat_wal to report WAL activity (Masahiro Ikeda)."9 Pre-PG14 deployments must aggregate pg_stat_statements.wal_records/wal_fpi/wal_bytes or read pg_current_wal_lsn() deltas manually.

Columns (PG14-18):

ColumnMeaning
wal_recordsTotal WAL records generated
wal_fpiFull-page images (one per page-modified-after-checkpoint)
wal_bytesCumulative WAL bytes
wal_buffers_fullTimes WAL buffers were full and a backend had to write WAL synchronously
wal_writeWAL writes from buffers (pre-PG18; in PG18+ this moved to pg_stat_io WAL rows)
wal_syncWAL fsyncs (pre-PG18)
wal_write_time / wal_sync_timeAggregate write/sync time, requires track_wal_io_timing = on (pre-PG18)
stats_resetReset timestamp

[!WARNING] PG18 removed wal_write / wal_sync / wal_write_time / wal_sync_time from pg_stat_wal These were relocated into pg_stat_io's new WAL rows. Monitoring queries that read these columns directly return NULL on PG18; rewrite to use pg_stat_io WHERE object IN ('wal') OR backend_type IN ('walwriter','walsender'). Cross-reference 33-wal.md.

pg_stat_bgwriter and pg_stat_checkpointer

Pre-PG17: a single pg_stat_bgwriter view held both the background-writer and checkpointer columns.

[!WARNING] PG17 split pg_stat_bgwriter into two views "Create system view pg_stat_checkpointer (Bharath Rupireddy, Anton A. Melnikov, Alexander Korotkov). Relevant columns have been removed from pg_stat_bgwriter and added to this new system view."10 Also: "Remove buffers_backend and buffers_backend_fsync from pg_stat_bgwriter ... These fields are considered redundant to similar columns in pg_stat_io."11 Monitoring queries written for PG16 silently return wrong values on PG17+. See 34-checkpoints-bgwriter.md for the full column-migration table.

PG17+ pg_stat_checkpointer columns: num_timed, num_requested, restartpoints_timed, restartpoints_req, restartpoints_done, write_time, sync_time, buffers_written, stats_reset.

[!NOTE] PostgreSQL 18 "Add column pg_stat_checkpointer.num_done to report the number of completed checkpoints."12 Note: num_timed and num_requested count both completed AND skipped checkpoints; num_done counts only completed. The difference (num_timed - num_done) is the skipped count. Also: "Add column pg_stat_checkpointer.slru_written to report SLRU buffers written."13

pg_stat_replication and pg_replication_slots

pg_stat_replication: one row per walsender on the primary. Key columns:

ColumnMeaning
pidWalsender PID
usename / application_name / client_addrClient identification
statestreaming, catchup, startup, backup, stopping
sent_lsnLSN of the last WAL byte sent over the wire
write_lsnLSN written to the standby's WAL files
flush_lsnLSN fsync'd on the standby
replay_lsnLSN replayed on the standby
write_lag / flush_lag / replay_lagTime lags as interval
sync_stateasync, potential, sync, quorum
sync_priorityPriority for sync replication
reply_timeTime of last reply from standby

pg_replication_slots (slot-side view): one row per replication slot.

ColumnMeaning
slot_name / plugin / slot_typeIdentification; slot_type is physical or logical
databaseDatabase for logical slots; NULL for physical
active / active_pidWhether currently being read
xmin / catalog_xminOldest XID this slot needs (drives autovacuum xmin horizon — cross-reference 27-mvcc-internals.md)
restart_lsnOldest WAL location required
confirmed_flush_lsnFor logical slots: oldest confirmed-flushed location
wal_statusPG13+: reserved, extended, unreserved, lost
safe_wal_sizePG13+: WAL bytes before max_slot_wal_keep_size causes invalidation

pg_stat_replication_slots (PG14+): per-slot logical-decoding statistics.

[!NOTE] PostgreSQL 14 "Add system view pg_stat_replication_slots to report replication slot activity."14 Tracks spill_txns, spill_count, spill_bytes, stream_txns, stream_count, stream_bytes, total_txns, total_bytes, stats_reset.

pg_stat_subscription and pg_stat_subscription_stats

pg_stat_subscription: one row per logical-replication subscription (and per parallel apply worker on PG16+ where leader_pid is non-NULL).

pg_stat_subscription_stats (PG15+): error counts per subscription. Columns: subid, subname, apply_error_count, sync_error_count, stats_reset. PG18 added confl_* columns for conflict tracking.

pg_stat_progress_* family

Verbatim docs note:15 "Whenever VACUUM is running, the pg_stat_progress_vacuum view will contain one row for each backend (including autovacuum worker processes) that is currently vacuuming." The same shape applies to all six progress views.

ViewTriggered byKey columns
pg_stat_progress_vacuumVACUUM (LAZY only, not FULL)pid, datid, relid, phase, heap_blks_total, heap_blks_scanned, heap_blks_vacuumed, index_vacuum_count, max_dead_tuple_bytes (PG17+; was max_dead_tuples), dead_tuple_bytes (PG17+), num_dead_item_ids (PG17+; was num_dead_tuples), indexes_total, indexes_processed (PG17+)
pg_stat_progress_analyzeANALYZEpid, datid, relid, phase, sample_blks_total, sample_blks_scanned, ext_stats_total, ext_stats_computed, child_tables_total, child_tables_done, current_child_table_relid
pg_stat_progress_create_indexCREATE INDEX [CONCURRENTLY], REINDEX [CONCURRENTLY]pid, datid, relid, index_relid, command, phase, lockers_total, lockers_done, current_locker_pid, blocks_total, blocks_done, tuples_total, tuples_done, partitions_total, partitions_done
pg_stat_progress_basebackuppg_basebackup (active server-side)pid, phase, backup_total, backup_streamed, tablespaces_total, tablespaces_streamed
pg_stat_progress_copyCOPY (PG14+)pid, datid, relid, command, type, bytes_processed, bytes_total, tuples_processed, tuples_excluded, tuples_skipped (PG17+)
pg_stat_progress_clusterCLUSTER, VACUUM FULLpid, datid, relid, command, phase, cluster_index_relid, heap_tuples_scanned, heap_tuples_written, heap_blks_total, heap_blks_scanned, index_rebuild_count

[!WARNING] pg_stat_progress_vacuum shows only LAZY VACUUM VACUUM FULL is implemented as a CLUSTER-style table rewrite, so it appears in pg_stat_progress_cluster with command = 'VACUUM FULL', not in pg_stat_progress_vacuum. Monitoring queries that join pg_stat_progress_vacuum to find "any vacuum" miss FULL operations.

Snapshot semantics and resetting

Verbatim docs rule:1 "When a server process is asked to display any of the accumulated statistics, accessed values are cached until the end of its current transaction in the default configuration ... You can invoke pg_stat_clear_snapshot() to discard the current transaction's statistics snapshot or cached values."

In practice:

  • Within a transaction, every pg_stat_* view returns the values from the first read in the transaction. To get fresh data without committing, call SELECT pg_stat_clear_snapshot(); first.
  • pg_stat_reset() resets all per-database stats (table, index, function counters in the current database). Per-cluster views (pg_stat_bgwriter, pg_stat_checkpointer, pg_stat_wal, pg_stat_io, pg_stat_archiver) reset independently with pg_stat_reset_shared(target text) where target is one of bgwriter, checkpointer, archiver, wal, io.
  • pg_stat_reset_single_table_counters(relid oid) resets one table.
  • pg_stat_reset_replication_slot(slot_name text) resets one logical-decoding slot's pg_stat_replication_slots row (PG14+).
  • pg_stat_reset_subscription_stats(subid oid) resets one subscription's error counts (PG15+).
  • pg_stat_reset_backend_stats(pid integer) resets per-backend I/O (PG18+).

Stats persist across clean restart (written to pg_stat/ directory at shutdown). A crash loses statistics for the current statistics-collector reporting interval — at most a few seconds.

Per-version timeline

VersionChanges
PG14pg_stat_wal view added; pg_stat_replication_slots view added; pg_stat_progress_copy view added; pg_locks.waitstart column added (per row showing when a wait began);16 session-statistics columns added to pg_stat_database (session_time, active_time, idle_in_transaction_time, sessions, sessions_abandoned, sessions_fatal, sessions_killed); query_id column added to pg_stat_activity (requires compute_query_id = on or auto); idle_session_timeout GUC added.17
PG15pg_stat_subscription_stats view added;18 log_checkpoints default changed to on;19 wait events for archive/restore commands added;20 jsonlog format added.
PG16pg_stat_io view added;5 last_seq_scan / last_idx_scan columns added to per-table/per-index stats;21 n_tup_newpage_upd column added to pg_stat_*_tables;22 leader_pid added to pg_stat_subscription;23 SpinDelay wait event added;24 pg_stat_io is the recommended replacement for pg_stat_bgwriter.buffers_backend.
PG17pg_stat_checkpointer view created (split from pg_stat_bgwriter); buffers_backend and buffers_backend_fsync removed from pg_stat_bgwriter; pg_stat_progress_vacuum columns renamed (max_dead_tuplesmax_dead_tuple_bytes, num_dead_tuplesnum_dead_item_ids, new dead_tuple_bytes); indexes_total and indexes_processed columns added to pg_stat_progress_vacuum; pg_stat_statements column renames (blk_read_timeshared_blk_read_time); savepoint names replaced with placeholders in pg_stat_statements; pg_wait_events system view added; pg_stat_progress_copy.tuples_skipped column added.
PG18pg_stat_io adds bytes columns (read_bytes, write_bytes, extend_bytes), removes op_bytes; pg_stat_io adds WAL rows; pg_stat_get_backend_io() added; pg_stat_reset_backend_stats(pid) added; pg_stat_checkpointer.num_done and .slru_written columns added; pg_stat_database.parallel_workers_to_launch / parallel_workers_launched columns added;25 pg_stat_wal loses wal_write/wal_sync/wal_write_time/wal_sync_time (relocated to pg_stat_io); pg_aios view added (async I/O subsystem); VACUUM and ANALYZE delay tracking with track_cost_delay_timing;26 pg_stat_subscription_stats gains conflict-tracking confl_* columns.

Examples / Recipes

Recipe 1: Currently-active queries with wait info

The single most-used diagnostic query. Run it whenever you need to answer "what is the cluster doing right now?"

SELECT
    pid,
    usename,
    application_name,
    state,
    wait_event_type,
    wait_event,
    now() - query_start         AS runtime,
    now() - xact_start          AS xact_age,
    LEFT(query, 80)             AS query
FROM pg_stat_activity
WHERE backend_type = 'client backend'
  AND state != 'idle'
ORDER BY query_start;

Reading the output:

  • state = 'active' AND wait_event_type IS NULL → backend is on CPU.
  • state = 'active' AND wait_event_type = 'IO' → waiting on disk (route to 32-buffer-manager.md).
  • state = 'active' AND wait_event_type = 'Lock' → blocked on a heavyweight lock (route to Recipe 2).
  • state = 'idle in transaction' → application started a transaction and is sitting on it (cross-reference 41-transactions.md for idle_in_transaction_session_timeout).

Recipe 2: Blocking chain — who is blocking whom?

SELECT
    blocked.pid                         AS blocked_pid,
    blocked.usename                     AS blocked_user,
    LEFT(blocked.query, 60)             AS blocked_query,
    blocking.pid                        AS blocking_pid,
    blocking.usename                    AS blocking_user,
    blocking.state                      AS blocking_state,
    LEFT(blocking.query, 60)            AS blocking_query,
    now() - blocked.xact_start          AS blocked_xact_age
FROM pg_stat_activity blocked
CROSS JOIN LATERAL unnest(pg_blocking_pids(blocked.pid)) AS blocking_pid
JOIN pg_stat_activity blocking ON blocking.pid = blocking_pid
WHERE pg_blocking_pids(blocked.pid) != '{}'
ORDER BY blocked.xact_start;

The verbatim docs rule on pg_blocking_pids:27 "One server process blocks another if it either holds a lock that conflicts with the blocked process's lock request (hard block), or is waiting for a lock that would conflict with the blocked process's lock request and is ahead of it in the wait queue (soft block)." Both forms appear in the LATERAL output.

Special case from the same docs paragraph: "when a prepared transaction holds a conflicting lock, it will be represented by a zero process ID." If the blocking_pid is 0, the blocker is a prepared transaction with no live session — find it via SELECT * FROM pg_prepared_xacts;. Cross-reference 43-locking.md gotcha #8.

Recipe 3: Long-running transactions holding xmin back

SELECT
    pid,
    usename,
    application_name,
    state,
    backend_xid,
    backend_xmin,
    age(backend_xmin)         AS xmin_age,
    now() - xact_start        AS xact_age,
    wait_event_type,
    wait_event,
    LEFT(query, 80)           AS query
FROM pg_stat_activity
WHERE backend_xmin IS NOT NULL
ORDER BY age(backend_xmin) DESC
LIMIT 20;

age(backend_xmin) measures how many XIDs have passed since this backend's snapshot was taken. A backend with a very large xmin_age is preventing VACUUM from cleaning dead tuples (cross-reference 27-mvcc-internals.md Rule 5 and 28-vacuum-autovacuum.md gotcha #11). Pair this with parallel checks on pg_replication_slots and pg_prepared_xacts since those also pin xmin horizon.

Recipe 4: Cache hit rate per database

SELECT
    datname,
    blks_hit + blks_read              AS total_block_accesses,
    round(100.0 * blks_hit / NULLIF(blks_hit + blks_read, 0), 2) AS hit_pct,
    blk_read_time + blk_write_time    AS io_time_ms,
    stats_reset
FROM pg_stat_database
WHERE datname IS NOT NULL
ORDER BY total_block_accesses DESC;

Cache hit rates below ~95% on an OLTP workload usually mean shared_buffers is undersized relative to the working set (cross-reference 32-buffer-manager.md and 54-memory-tuning.md). The blk_read_time and blk_write_time columns require track_io_timing = on and are essential for distinguishing "I have a lot of disk reads that are fast" from "I have disk reads that are slow."

Recipe 5: Find unused indexes (with caveats)

SELECT
    schemaname || '.' || relname  AS table,
    indexrelname                   AS index,
    pg_size_pretty(pg_relation_size(indexrelid)) AS index_size,
    idx_scan,
    last_idx_scan,
    CASE WHEN idx_scan = 0 THEN 'NEVER SCANNED'
         ELSE 'used'
    END AS status
FROM pg_stat_user_indexes
JOIN pg_index USING (indexrelid)
WHERE NOT indisunique
  AND NOT indisprimary
  AND pg_relation_size(indexrelid) > 1024 * 1024  -- > 1 MB
ORDER BY idx_scan, pg_relation_size(indexrelid) DESC;

[!WARNING] Three caveats before dropping any index

  1. Replicas accumulate independent index-scan counters. A query on a read replica increments idx_scan on the replica, not on the primary. If you only check the primary, you may drop an index a reporting replica relies on. Run this query on every replica before dropping.
  2. Unique and PK-backing indexes show idx_scan = 0 for tables where lookups go through other paths. The NOT indisunique AND NOT indisprimary filter excludes them, but exclusion-constraint-backing indexes and FK-target indexes still need verification.
  3. last_idx_scan is PG16+ only. Pre-PG16 you cannot distinguish "never used" from "used last year, hasn't been scanned this week." Reset stats and observe a representative time window.

Recipe 6: Tables overdue for autovacuum

SELECT
    schemaname || '.' || relname              AS table,
    pg_size_pretty(pg_total_relation_size(relid)) AS total_size,
    n_live_tup,
    n_dead_tup,
    round(100.0 * n_dead_tup / NULLIF(n_live_tup, 0), 1) AS dead_pct,
    n_mod_since_analyze,
    last_autovacuum,
    last_autoanalyze,
    CASE
        WHEN last_autovacuum IS NULL THEN 'never autovacuumed'
        WHEN last_autovacuum < now() - interval '1 day' THEN 'overdue'
        ELSE 'recent'
    END AS state
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY dead_pct DESC NULLS LAST, n_dead_tup DESC
LIMIT 20;

Use this to find the actual victims of broken autovacuum. The decision tree:

  • High dead_pct AND last_autovacuum recent → autovacuum is running but not keeping up; raise per-table autovacuum_vacuum_scale_factor aggressiveness or autovacuum_vacuum_cost_limit (cross-reference 28-vacuum-autovacuum.md Recipe 1).
  • High dead_pct AND last_autovacuum IS NULL for hours → autovacuum is being canceled by lock conflicts OR the xmin horizon is pinned (run Recipe 3).
  • High n_dead_tup AND low n_live_tup → likely a recently-truncated or recently-deleted table; not necessarily a problem.

Recipe 7: Watch a running VACUUM in real time

SELECT
    p.pid,
    p.datname,
    p.relid::regclass             AS table,
    p.phase,
    pg_size_pretty(p.heap_blks_total::bigint * 8192)   AS heap_total,
    pg_size_pretty(p.heap_blks_scanned::bigint * 8192) AS heap_scanned,
    round(100.0 * p.heap_blks_scanned / NULLIF(p.heap_blks_total, 0), 1) AS heap_pct,
    p.index_vacuum_count,
    p.indexes_total,                  -- PG17+
    p.indexes_processed,              -- PG17+
    p.dead_tuple_bytes,               -- PG17+
    p.max_dead_tuple_bytes,           -- PG17+ (was max_dead_tuples)
    a.wait_event_type,
    a.wait_event,
    now() - a.query_start             AS runtime
FROM pg_stat_progress_vacuum p
JOIN pg_stat_activity a ON a.pid = p.pid
ORDER BY a.query_start;

Phase interpretation:

  • initializing — Almost always brief.
  • scanning heap — The main pass.
  • vacuuming indexes — Per-index cleanup. If index_vacuum_count > 1 for one VACUUM run, maintenance_work_mem is too small and VACUUM has to scan indexes multiple times to clean accumulated dead tuples.
  • vacuuming heap / cleaning up indexes / truncating heap / performing final cleanup — Wrap-up phases.

If wait_event_type = 'Lock' or wait_event_type = 'BufferPin' for a long-running anti-wraparound VACUUM, something is blocking it. Anti-wraparound VACUUM cannot be canceled by lock_timeout (cross-reference 29-transaction-id-wraparound.md gotcha #4).

Recipe 8: Streaming replication lag per standby

SELECT
    application_name,
    client_addr,
    state,
    sync_state,
    pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) AS lag_bytes,
    write_lag,
    flush_lag,
    replay_lag,
    reply_time,
    now() - reply_time AS time_since_reply
FROM pg_stat_replication
ORDER BY pg_wal_lsn_diff(sent_lsn, replay_lsn) DESC;

The verbatim docs note (paraphrased from pg_stat_replication): the *_lag time columns are NULL when the standby has nothing pending. Reading replay_lag IS NULL as "no lag" is correct; reading replay_lag IS NULL while pg_wal_lsn_diff(sent_lsn, replay_lsn) > 0 means the lag exists in bytes but the time has not yet been computed because the standby has not yet ACKed. Cross-reference 73-streaming-replication.md.

Recipe 9: Slot WAL retention — abandoned slots burning disk

SELECT
    slot_name,
    slot_type,
    database,
    active,
    active_pid,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retention_bytes,
    wal_status,
    pg_size_pretty(safe_wal_size) AS safe_wal_size,
    age(xmin)         AS xmin_age,
    age(catalog_xmin) AS catalog_xmin_age
FROM pg_replication_slots
ORDER BY pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn) DESC;

A slot with active = false and a large retention_bytes is the canonical "disk filling up" emergency. Drop it with pg_drop_replication_slot('slot_name') after confirming the slot is genuinely abandoned (the standby or subscriber it pointed at is gone or will not be returning). The wal_status column (PG13+) shows reserved (within wal_keep_size), extended (using slot retention), unreserved (close to max_slot_wal_keep_size), or lost (slot will be invalidated). Cross-reference 75-replication-slots.md.

Recipe 10: Sessions waiting on the same lock — identify the head

SELECT
    a.pid,
    a.usename,
    a.state,
    a.wait_event_type,
    a.wait_event,
    l.locktype,
    l.relation::regclass        AS relation,
    l.mode                      AS lock_mode,
    l.granted,
    now() - a.xact_start        AS xact_age,
    LEFT(a.query, 60)           AS query
FROM pg_locks l
JOIN pg_stat_activity a ON a.pid = l.pid
WHERE l.locktype IN ('relation', 'tuple', 'transactionid')
ORDER BY l.relation, l.granted DESC, a.xact_start;

This combines lock-level visibility with session-level activity. The first row (granted = true) for each relation is the holder; subsequent rows (granted = false) are the queue. Cross-reference 43-locking.md Recipe 1.

Recipe 11: PG16+ per-relation I/O attribution via pg_stat_io

-- PG16-17 (op_bytes column exists):
SELECT
    backend_type,
    object,
    context,
    reads, writes, extends, hits,
    pg_size_pretty(reads::bigint * op_bytes)  AS read_volume,
    pg_size_pretty(writes::bigint * op_bytes) AS write_volume,
    evictions, reuses, fsyncs,
    round(read_time::numeric, 1)  AS read_time_ms,
    round(write_time::numeric, 1) AS write_time_ms
FROM pg_stat_io
WHERE reads > 0 OR writes > 0
ORDER BY reads + writes DESC;

-- PG18+ (use new bytes columns):
SELECT
    backend_type,
    object,
    context,
    reads, writes, extends, hits,
    pg_size_pretty(read_bytes)   AS read_volume,
    pg_size_pretty(write_bytes)  AS write_volume,
    pg_size_pretty(extend_bytes) AS extend_volume,
    evictions, reuses, fsyncs
FROM pg_stat_io
WHERE reads > 0 OR writes > 0
ORDER BY read_bytes + write_bytes DESC;

Reading the matrix: high (backend_type = 'client backend', context = 'normal', writes > 0) means application backends are doing their own dirty-buffer writeback (checkpointer/bgwriter cannot keep up — cross-reference 34-checkpoints-bgwriter.md). High (backend_type = 'autovacuum worker', context = 'vacuum') is normal during autovacuum runs. High (context = 'bulkread') matches SELECT * of large tables and uses the ring buffer.

Recipe 12: Diagnose temp-file spillage

SELECT
    datname,
    temp_files,
    pg_size_pretty(temp_bytes)            AS temp_bytes,
    round(temp_bytes::numeric / NULLIF(temp_files, 0)) AS avg_temp_file_bytes,
    stats_reset,
    now() - stats_reset                    AS measurement_period
FROM pg_stat_database
WHERE temp_files > 0
ORDER BY temp_bytes DESC;

Non-zero temp_files means sorts, hashes, or materialized intermediate results spilled to disk. Route to 54-memory-tuning.md Recipe 5 (work_mem tuning) and 57-pg-stat-statements.md Recipe 4 (find which queries are spilling).

Recipe 13: Killing a backend safely

-- Soft cancel: ask the backend to abort its current query
SELECT pg_cancel_backend(<pid>);

-- Hard terminate: kill the backend connection (use sparingly)
SELECT pg_terminate_backend(<pid>);

[!WARNING] Special backend types Do not pg_terminate_backend walsenders or logical-replication apply workers without understanding replication implications — termination drops the connection and may cause the standby/subscriber to re-stream from restart_lsn. Identify these by backend_type in pg_stat_activity. Autovacuum workers can be safely canceled but will be re-scheduled. Cross-reference 43-locking.md gotcha #20.

Gotchas / Anti-patterns

  1. pg_stat_activity snapshots are transaction-cached. Reading the view twice in the same transaction returns identical results. Use SELECT pg_stat_clear_snapshot(); to discard the snapshot or run each query in autocommit mode (the default in psql).

  2. state = 'active' does NOT mean "on CPU." Active backends can be waiting on Lock, IO, LWLock, IPC, etc. Always check wait_event_type alongside state.

  3. query is truncated. The default track_activity_query_size = 1024 bytes. Long queries are clipped silently. Raise the GUC if you regularly diagnose multi-KB queries.

  4. backend_xid is NULL for read-only transactions. A backend that has only executed SELECTs has not acquired a top-level XID. backend_xmin may still be non-NULL (it's the snapshot horizon). Use backend_xmin for xmin-horizon analysis, not backend_xid.

  5. idx_scan = 0 is NOT proof the index is unused. It is proof the index has not been scanned since the last pg_stat_reset() ON THIS NODE. Replicas track independently. Stats reset on pg_upgrade. The last_idx_scan column (PG16+) gives a more honest "never used since this column was introduced" signal.

  6. Monitoring queries written for PG≤16 break on PG17+ in three ways: (a) pg_stat_bgwriter.buffers_backend / buffers_backend_fsync removed — read pg_stat_io instead; (b) pg_stat_bgwriter.checkpoints_* columns moved to pg_stat_checkpointer; (c) pg_stat_progress_vacuum.max_dead_tuples / num_dead_tuples renamed to max_dead_tuple_bytes / num_dead_item_ids. The PG16-era query silently returns NULL or zero from these columns instead of erroring.

  7. pg_stat_io.op_bytes was removed in PG18. Computing volume as reads * op_bytes returns NULL on PG18. Use read_bytes / write_bytes / extend_bytes directly.

  8. pg_stat_progress_vacuum does NOT show VACUUM FULL. VACUUM FULL is implemented as a CLUSTER and reports through pg_stat_progress_cluster with command = 'VACUUM FULL'.

  9. pg_stat_activity shows logical-replication apply workers as backend_type = 'logical replication worker' but the query column may be empty or hold the most recent applied statement. Replication apply progress lives in pg_stat_subscription, not in pg_stat_activity.query.

  10. pg_blocking_pids returns duplicate PIDs for parallel queries. A single parallel-query leader's blocking set may list the same blocker multiple times (once per parallel worker the leader spawned). The docs verbatim:27 "When using parallel queries the result always lists client-visible process IDs (that is, pg_backend_pid results) even if the actual lock is held or awaited by a child worker process. As a result of that, there may be duplicated PIDs in the result."

  11. pg_blocking_pids zero PID = prepared transaction. "Also note that when a prepared transaction holds a conflicting lock, it will be represented by a zero process ID." Find via SELECT * FROM pg_prepared_xacts;.

  12. Cumulative counters wrap at bigint, eventually. pg_stat_database.tup_returned, pg_stat_user_tables.seq_tup_read, pg_stat_wal.wal_records are bigints. On extremely-high-traffic clusters, they wrap after years of uptime. A negative delta = current - prior between two snapshots is a wraparound signal, not a bug.

  13. stats_reset is per-collector-target, not per-view. Resetting pg_stat_database does not reset pg_stat_user_tables or pg_stat_user_indexes. Each per-database stat target resets together; cross-cluster shared stats (bgwriter, checkpointer, wal, io, archiver) reset independently via pg_stat_reset_shared(target text).

  14. pg_stat_replication.replay_lag is NULL on idle standbys. A standby that has caught up to the primary's LSN and is waiting has replay_lag = NULL. Reading this as "no lag" is correct, but it is NOT equivalent to "the standby is healthy" — a stopped standby also has replay_lag = NULL because no recent WAL has been streamed.

  15. pg_stat_progress_create_index shows phase = 'waiting for writers before build' indefinitely if a long transaction is open. CREATE INDEX CONCURRENTLY's wait-for-writers phase is the most common cause of stuck CIC. Identify the blocker via current_locker_pid joined to pg_stat_activity. Cross-reference 26-index-maintenance.md Recipe 8.

  16. pg_stat_subscription does NOT contain error counts. Subscription errors live in pg_stat_subscription_stats (PG15+). On PG14 there is no SQL-visible subscription error counter; read the logs.

  17. pg_stat_wal.wal_buffers_full > 0 on a busy cluster signals undersized wal_buffers. Cross-reference 33-wal.md for wal_buffers tuning.

  18. pg_stat_database.conflicts > 0 only matters on standbys. On the primary, this is always zero. Recovery conflicts on a standby (a primary VACUUM removed rows that a standby query was using) are normal under load; raise them above zero by setting hot_standby_feedback = on (with cost — see 73-streaming-replication.md).

  19. pg_stat_database.checksum_failures > 0 is an emergency. This is page-level corruption. Cross-reference 88-corruption-recovery.md. Treat the cluster as compromised until you have isolated the corrupted relation via pg_amcheck.

  20. pg_stat_user_indexes.idx_tup_fetch is zero for Index Only Scans without heap fetches. A working index-only-scan plan increments idx_tup_read but not idx_tup_fetch. Concluding "this index is not returning rows" from idx_tup_fetch = 0 is wrong; check idx_scan and idx_tup_read instead.

  21. pg_stat_progress_basebackup only shows server-side basebackup operations. A client-side pg_basebackup running in another network namespace does not appear here.

  22. pg_stat_activity.backend_type = 'client backend' excludes background workers but INCLUDES parallel workers (with non-NULL leader_pid). Filter leader_pid IS NULL to count only true client backends and not double-count parallel groups.

  23. pg_stat_* queries themselves perturb the statistics they read. A monitoring agent that runs Recipe 1 every second adds 60 backends/minute of state = 'active' activity to pg_stat_activity and increments xact_commit on pg_stat_database (assuming each query is its own transaction). For very-high-cardinality cluster-wide monitoring, sample at intervals matching your retention granularity rather than continuously.

See Also

Sources

Footnotes

  1. PostgreSQL 16 documentation, Chapter 28: Monitoring Database Activity. https://www.postgresql.org/docs/16/monitoring-stats.html — verbatim: "The wait_event and state columns are independent. If a backend is in the active state, it may or may not be waiting on some event." and "when a server process is asked to display any of the accumulated statistics, accessed values are cached until the end of its current transaction in the default configuration ... You can invoke pg_stat_clear_snapshot() to discard the current transaction's statistics snapshot or cached values." 2 3 4 5

  2. PostgreSQL 16, pg_stat_activity view section. https://www.postgresql.org/docs/16/monitoring-stats.html#MONITORING-PG-STAT-ACTIVITY-VIEW — verbatim: "The pg_stat_activity view will have one row per server process, showing information related to the current activity of that process."

  3. PostgreSQL 17 release notes. https://www.postgresql.org/docs/release/17.0/ — verbatim: "Add system view pg_wait_events that reports wait event types (Bertrand Drouvot). This is useful for adding descriptions to wait events reported in pg_stat_activity."

  4. PostgreSQL 14 release notes. https://www.postgresql.org/docs/release/14.0/ — verbatim: "Add session statistics to the pg_stat_database system view (Laurenz Albe)."

  5. PostgreSQL 16 release notes. https://www.postgresql.org/docs/release/16.0/ — verbatim: "Add system view pg_stat_io view to track I/O statistics (Melanie Plageman)." 2

  6. PostgreSQL 18 release notes. https://www.postgresql.org/docs/release/18.0/ — verbatim: "Add pg_stat_io columns to report I/O activity in bytes (Nazir Bilal Yavuz). The new columns are read_bytes, write_bytes, and extend_bytes. The op_bytes column, which always equalled BLCKSZ, has been removed."

  7. PostgreSQL 18 release notes. https://www.postgresql.org/docs/release/18.0/ — verbatim: "Add WAL I/O activity rows to pg_stat_io ... This includes WAL receiver activity and a wait event for such writes."

  8. PostgreSQL 18 release notes. https://www.postgresql.org/docs/release/18.0/ — verbatim: "Add per-backend I/O statistics reporting (Bertrand Drouvot). The statistics are accessed via pg_stat_get_backend_io(). Per-backend I/O statistics can be cleared via pg_stat_reset_backend_stats()."

  9. PostgreSQL 14 release notes. https://www.postgresql.org/docs/release/14.0/ — verbatim: "Add system view pg_stat_wal to report WAL activity (Masahiro Ikeda)."

  10. PostgreSQL 17 release notes. https://www.postgresql.org/docs/release/17.0/ — verbatim: "Create system view pg_stat_checkpointer (Bharath Rupireddy, Anton A. Melnikov, Alexander Korotkov). Relevant columns have been removed from pg_stat_bgwriter and added to this new system view."

  11. PostgreSQL 17 release notes. https://www.postgresql.org/docs/release/17.0/ — verbatim: "Remove buffers_backend and buffers_backend_fsync from pg_stat_bgwriter ... These fields are considered redundant to similar columns in pg_stat_io."

  12. PostgreSQL 18 release notes. https://www.postgresql.org/docs/release/18.0/ — verbatim: "Add column pg_stat_checkpointer.num_done to report the number of completed checkpoints (Anton A. Melnikov). Columns num_timed and num_requested count both completed and skipped checkpoints."

  13. PostgreSQL 18 release notes. https://www.postgresql.org/docs/release/18.0/ — verbatim: "Add column pg_stat_checkpointer.slru_written to report SLRU buffers written (Nitin Jadhav). Also, modify the checkpoint server log message to report separate shared buffer and SLRU buffer values."

  14. PostgreSQL 14 release notes. https://www.postgresql.org/docs/release/14.0/ — verbatim: "Add system view pg_stat_replication_slots to report replication slot activity."

  15. PostgreSQL 16, Progress Reporting chapter. https://www.postgresql.org/docs/16/progress-reporting.html — verbatim: "Whenever VACUUM is running, the pg_stat_progress_vacuum view will contain one row for each backend (including autovacuum worker processes) that is currently vacuuming."

  16. PostgreSQL 14 release notes. https://www.postgresql.org/docs/release/14.0/ — verbatim: "Add lock wait start time to pg_locks (Atsushi Torikoshi)."

  17. PostgreSQL 14 release notes. https://www.postgresql.org/docs/release/14.0/ — verbatim: "Add server parameter idle_session_timeout to close idle sessions. This is similar to idle_in_transaction_session_timeout but applies to any idle session."

  18. PostgreSQL 15 release notes. https://www.postgresql.org/docs/release/15.0/ — verbatim: "Add system view pg_stat_subscription_stats to report on subscriber activity."

  19. PostgreSQL 15 release notes. https://www.postgresql.org/docs/release/15.0/ — verbatim: "This changes the default of log_checkpoints to on and that of log_autovacuum_min_duration to 10 minutes. This will cause even an idle server to generate some log output, which can be disabled by setting these parameters to off and -1, respectively."

  20. PostgreSQL 15 release notes. https://www.postgresql.org/docs/release/15.0/ — verbatim: "Add wait events for local shell commands ... used when calling archive_command, archive_cleanup_command, restore_command and recovery_end_command."

  21. PostgreSQL 16 release notes. https://www.postgresql.org/docs/release/16.0/ — verbatim: "Record statistics on the last sequential and index scans on tables (Dave Page). The columns pg_stat_*_tables.last_seq_scan, last_idx_scan, and pg_stat_*_indexes.last_idx_scan were added for this purpose."

  22. PostgreSQL 16 release notes. https://www.postgresql.org/docs/release/16.0/ — verbatim: "Record statistics on the occurrence of updated rows moving to new pages (Corey Huinker). The pg_stat_*_tables column is n_tup_newpage_upd."

  23. PostgreSQL 16 release notes. https://www.postgresql.org/docs/release/16.0/ — verbatim: "Column leader_pid was added to system view pg_stat_subscription to track parallel activity."

  24. PostgreSQL 16 release notes. https://www.postgresql.org/docs/release/16.0/ — verbatim: "Add wait event SpinDelay to report spinlock sleep delays."

  25. PostgreSQL 18 release notes. https://www.postgresql.org/docs/release/18.0/ — verbatim: "Add columns to pg_stat_database to report parallel worker activity (Benoit Lobréau). The new columns are parallel_workers_to_launch and parallel_workers_launched."

  26. PostgreSQL 18 release notes. https://www.postgresql.org/docs/release/18.0/ — verbatim: "Add delay time reporting to VACUUM and ANALYZE ... tracking must be enabled with the server variable track_cost_delay_timing."

  27. PostgreSQL 16, System Information Functions. https://www.postgresql.org/docs/16/functions-info.html — verbatim: "One server process blocks another if it either holds a lock that conflicts with the blocked process's lock request (hard block), or is waiting for a lock that would conflict with the blocked process's lock request and is ahead of it in the wait queue (soft block). When using parallel queries the result always lists client-visible process IDs ... there may be duplicated PIDs in the result. Also note that when a prepared transaction holds a conflicting lock, it will be represented by a zero process ID." 2

references

01-syntax-ddl.md

02-syntax-dql.md

03-syntax-dml.md

04-ctes.md

05-views.md

06-functions.md

07-procedures.md

08-plpgsql.md

09-procedural-languages.md

10-dynamic-sql.md

11-window-functions.md

12-aggregates-grouping.md

13-cursors-and-prepares.md

14-data-types-builtin.md

15-data-types-custom.md

16-arrays.md

17-json-jsonb.md

18-uuid-numeric-money.md

19-timestamp-timezones.md

20-text-search.md

21-hstore.md

22-indexes-overview.md

23-btree-indexes.md

24-gin-gist-indexes.md

25-brin-hash-spgist-bloom-indexes.md

26-index-maintenance.md

27-mvcc-internals.md

28-vacuum-autovacuum.md

29-transaction-id-wraparound.md

30-hot-updates.md

31-toast.md

32-buffer-manager.md

33-wal.md

34-checkpoints-bgwriter.md

35-partitioning.md

36-inheritance.md

37-constraints.md

38-foreign-keys-deep.md

39-triggers.md

40-event-triggers.md

41-transactions.md

42-isolation-levels.md

43-locking.md

44-advisory-locks.md

45-listen-notify.md

46-roles-privileges.md

47-row-level-security.md

48-authentication-pg-hba.md

49-tls-ssl.md

50-encryption-pgcrypto.md

51-pgaudit.md

52-rules-system.md

53-server-configuration.md

54-memory-tuning.md

55-statistics-planner.md

56-explain.md

57-pg-stat-statements.md

58-performance-diagnostics.md

59-planner-tuning.md

60-parallel-query.md

61-jit-compilation.md

62-tablespaces.md

63-internals-architecture.md

64-system-catalogs.md

65-collations-encoding.md

66-bulk-operations-copy.md

67-cli-tools.md

68-pgbench.md

69-extensions.md

70-fdw.md

71-large-objects.md

72-extension-development.md

73-streaming-replication.md

74-logical-replication.md

75-replication-slots.md

76-logical-decoding.md

77-standby-failover.md

78-ha-architectures.md

79-patroni.md

80-connection-pooling.md

81-pgbouncer.md

82-monitoring.md

83-backup-pg-dump.md

84-backup-physical-pitr.md

85-backup-tools.md

86-pg-upgrade.md

87-major-version-upgrade.md

88-corruption-recovery.md

89-pg-rewind.md

90-disaster-recovery.md

91-docker-postgres.md

92-kubernetes-operators.md

93-pg-trgm.md

94-pgvector.md

95-postgis.md

96-timescaledb.md

97-citus.md

98-pg-cron.md

99-pg-partman.md

100-pg-versions-features.md

101-managed-vs-baremetal.md

102-skill-cookbook.md

SKILL.md

tile.json