Instiq
Chapter 2 · Performance monitoring·v1.0.0·Updated 7/8/2026·~13 min

What's changed: Initial version (topic G2)

2.1Access Statistics Views

Key points

Learn the family of statistics views that reflect the server's "right now": pg_stat_activity (wait_event_type/wait_event) for connected sessions, pg_locks for lock contention, pg_stat_database for per-database aggregates, pg_stat_all_tables for table access patterns, pg_statio_all_tables for physical I/O, pg_stat_archiver for WAL archiving, and pg_stat_bgwriter for checkpoint/buffer writeback activity.

When a report comes in that "the server is slow," the first move is the statistics views, not guesswork. PostgreSQL continuously aggregates and exposes a running server's internal state—connections, lock contention, table access patterns, physical I/O—under the pg_stat_* namespace. Accurately mapping which view reflects what is the starting point of performance monitoring.

2.1.1pg_stat_activity and pg_locks

  • pg_stat_activity shows every currently connected session, one row per process: pid, usename, state (active/idle/idle in transaction/etc.), query (the running or most recent SQL). It also exposes wait_event_type (a category such as Lock/LWLock/IO/Client) and wait_event (the specific wait event name), letting you pin down exactly what a session is waiting on.
  • pg_locks enumerates lock information held or requested inside the server, one row per lock: locktype, relation, pid, mode (AccessShareLock through AccessExclusiveLock), and granted (true = acquired, false = waiting). Joining it to pg_stat_activity on pid reveals which query is blocked waiting on which lock—the standard combination for deadlock investigation and first-pass lock-contention triage.

2.1.2pg_stat_database, table/I-O stats, archiver, and bgwriter

  • pg_stat_database aggregates per database: xact_commit/xact_rollback (commit/rollback counts), blks_read/blks_hit (physical reads vs. buffer hits), deadlocks, and temp_files/temp_bytes (temp-file usage). It is the frequent source for computing the cache hit ratio (blks_hit / (blks_hit + blks_read)).
  • pg_stat_all_tables captures per-table access patterns: seq_scan/seq_tup_read (sequential scan count and rows read), idx_scan/idx_tup_fetch (index usage), n_tup_ins/n_tup_upd/n_tup_del, n_dead_tup (dead tuple count—the key input for deciding whether VACUUM is needed), and last_vacuum/last_autovacuum/last_analyze/last_autoanalyze (timestamps of each maintenance operation's last run).
  • pg_statio_all_tables is dedicated to physical I/O: heap_blks_read/heap_blks_hit (disk reads vs. cache hits on the table heap) and idx_blks_read/idx_blks_hit (the index side). When you need per-table cache efficiency, this—not pg_stat_all_tables—is the view to consult.
  • pg_stat_archiver tracks WAL archiving success/failure (archived_count, last_archived_wal, failed_count, last_failed_wal)—used to detect archive_command failures. pg_stat_bgwriter gives checkpoint/background writer writeback statistics (checkpoints_timed/checkpoints_req = scheduled vs. requested checkpoint counts; buffers_checkpoint/buffers_clean/buffers_backend = breakdown by writer source), which helps judge whether checkpoints are firing too often (a high checkpoints_req hints at an undersized max_wal_size).
Exam point

The staples: use pg_stat_activity's wait_event_type/wait_event to identify why a session is waiting; join pg_locks to pg_stat_activity on pid to identify the blocking query; n_dead_tup informs whether VACUUM is needed; pg_statio_all_tables is dedicated to physical I/O (pg_stat_all_tables is about access counts); a high checkpoints_req hints at an undersized max_wal_size. The mapping between a view's name suffix (_activity/_database/_all_tables/_bgwriter) and its aggregation granularity recurs often.

Walk through a real triage flow. Given a report that "a specific query never returns," first survey the wait state of every active session with SELECT pid, state, wait_event_type, wait_event, query FROM pg_stat_activity WHERE state != 'idle';. If wait_event_type is Lock, that session is waiting on some lock, so next join pg_locks to pg_stat_activity on pid and look for rows with granted = false. The pid you find there belongs to the blocking session that is holding a lock while its transaction has not finished. That blocking session's state is often idle in transaction—the classic pattern of "issued BEGIN and forgot to COMMIT/ROLLBACK." Once the cause is identified, the emergency response is to cancel that backend's query with pg_cancel_backend() (Gold G1 scope). If instead wait_event_type is IO, disk I/O itself is likely the bottleneck, so check pg_statio_all_tables for an abnormally high heap_blks_read on the affected table. On the other hand, given a report that "writes to a specific table are slow," check n_dead_tup in pg_stat_all_tables; if dead tuples are piling up and last_autovacuum is stale (autovacuum is falling behind), you can conclude the table needs VACUUM tuning (G1/G3 scope). The key mindset is that a statistics view alone rarely explains the cause—you narrow a hypothesis by combining several views together.

ViewGranularityPrimary use
pg_stat_activitySession (process)Identify wait reason (wait_event_type/wait_event)
pg_locksLock rowIdentify blocking session (join on pid)
pg_stat_databaseDatabaseCache hit ratio, deadlock count
pg_stat_all_tablesTableAccess pattern, n_dead_tup (VACUUM decision)
pg_statio_all_tablesTablePhysical I/O (heap/idx blks_read/hit)
Warning

Trap: "pg_stat_all_tables tells you disk I/O load" is imprecise—pg_stat_all_tables tracks access counts (seq_scan/idx_scan/etc.); the view dedicated to physical I/O (block reads/hits) is pg_statio_all_tables. Also, "a pg_locks row with granted=false shows the session holding the lock" is wrong—granted=false means requesting (i.e., the blocked side); to find the holder (the blocking session), you must look for a granted=true row on the same relation.

Diagram of pg_stat_activity/pg_locks session monitoring, pg_stat_database/pg_stat_all_tables table stats, and pg_stat_archiver/pg_stat_bgwriter.
wait_event shows what a session is currently waiting on

2.1.3Section summary

  • pg_stat_activity = per-session (wait_event_type/wait_event identify the wait reason). pg_locks = lock rows (granted=false is the waiting side; join on pid to pg_stat_activity)
  • pg_stat_database = per-DB aggregates; pg_stat_all_tables = access counts + n_dead_tup; pg_statio_all_tables = physical I/O only; pg_stat_archiver = WAL archiving success/failure; pg_stat_bgwriter = checkpoint/writeback stats

Sign in to track progress — Log in.

Quick check

(just a quick review)

Q1. A specific query on the production DB has not returned for a long time. You want to first identify what the session is waiting on. Which view and columns should you check first?

Q2. You joined pg_locks to pg_stat_activity on pid and found a session with a granted=false row. Which statement about this session is correct?

Q3. Updates to a table are gradually getting slower. You want to check whether VACUUM is falling behind. Which view and columns should you check?

Check your understandingPractice questions for Chapter 2: Performance monitoring