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

Keep track of your progress

The full study guide is free to read. Sign up free to practice with the question bank, track what you have read, review your mistakes, and highlight passages.