PostgreSQLXVI · Observability, Logging and AlertingObservability
The statistics views that matter, and the ones that changed
What you'll learn
- Name the views that answer operational questions and what each is for
- Handle the pg_stat_bgwriter split correctly
- Interpret counters as rates rather than as values
- Know which numbers are cumulative and when they reset
Prerequisites
Verified against PostgreSQL 18.x · PostgreSQL (comparison targets) 17.11, 16.15 · PostgreSQL (support calendar) 18, 17, 16, 15, 14 supported · pgBackRest 2.59.1 · PgBouncer 1.25.2 · Patroni 4.1.5 · Ubuntu (host baseline) 26.04 LTS · 2026-08-27
There are dozens of statistics views. A small number answer questions you will actually ask.
The map
| View | Answers |
|---|---|
pg_stat_activity | What is happening now |
pg_stat_database | Cluster-level health: cache, deadlocks, temp files, sessions |
pg_stat_all_tables | Which tables are churned, scanned, vacuumed |
pg_stat_all_indexes | Which indexes are used, and which are not |
pg_statio_all_tables | Where physical reads go |
pg_stat_checkpointer | Checkpoint frequency and cost |
pg_stat_bgwriter | Background writer only, since 17 |
pg_stat_wal | WAL generation, lesson XII-02 |
pg_stat_io | I/O by backend type and context, lesson XII-05 |
pg_stat_replication | Standbys, Part XIV |
pg_stat_archiver | Archiving, lesson XIII-05 |
pg_stat_statements | Query aggregate cost, lesson XVI-05 |
The split that breaks dashboards
$ SELECT * FROM pg_stat_bgwriter;-[ RECORD 1 ]----+------------------------------
buffers_clean | 1354
maxwritten_clean | 9
buffers_alloc | 61370
stats_reset | 2026-08-27 22:14:46.039111+00$ SELECT * FROM pg_stat_checkpointer;-[ RECORD 1 ]-------+------------------------------
num_timed | 4
num_requested | 2
num_done | 3
restartpoints_timed | 41
restartpoints_req | 0
restartpoints_done | 5
write_time | 298152
sync_time | 44
buffers_written | 13170
slru_written | 23
stats_reset | 2026-08-27 22:14:46.039111+00The restartpoint_* columns are worth noticing: this node was a standby
until the promotion in Part XV, and restartpoints are what a standby
performs in place of checkpoints. restartpoints_timed 41 against
restartpoints_done 5 is the normal pattern — most are skipped because
replay has not advanced far enough to make one useful.
pg_stat_database
$ SELECT datname, numbackends, xact_commit, xact_rollback, blks_hit, blks_read,
round(100.0*blks_hit/NULLIF(blks_hit+blks_read,0),2) AS cache_hit_pct,
deadlocks, temp_files, pg_size_pretty(temp_bytes) AS temp,
conflicts, checksum_failures
FROM pg_stat_database WHERE datname='postgres'; datname | numbackends | xact_commit | xact_rollback | blks_hit | blks_read | cache_hit_pct | deadlocks | temp_files | temp | conflicts | checksum_failures
----------+-------------+-------------+---------------+----------+-----------+---------------+-----------+------------+---------+-----------+-------------------
postgres | 1 | 50559 | 1 | 1447898 | 20762 | 98.59 | 0 | 4 | 9808 kB | 0 | 0| Column | Alert when |
|---|---|
deadlocks | Increasing at all. Part IX |
temp_files / temp_bytes | Increasing. work_mem too small, lesson XI-05 |
checksum_failures | Non-zero. Ever. Part XVIII |
conflicts | Increasing on a standby. Lesson XIV-07 |
xact_rollback | Rising share — application errors |
cache_hit_pct | Falling, as a trend |
$ SELECT datname, session_time::int, active_time::int, idle_in_transaction_time::int, sessions, sessions_abandoned, sessions_fatal, sessions_killed FROM pg_stat_database WHERE datname='postgres'; datname | session_time | active_time | idle_in_transaction_time | sessions | sessions_abandoned | sessions_fatal | sessions_killed
----------+--------------+-------------+--------------------------+----------+--------------------+----------------+-----------------
postgres | 521821 | 506687 | 11760 | 271 | 0 | 0 | 0sessions_abandoned, sessions_fatal and sessions_killed at zero is
what health looks like. Any of them climbing is a real signal:
abandoned means clients disconnected without closing, fatal means the
server ended them, killed means an administrator did.
idle_in_transaction_time against active_time — 11.8 s against
506.7 s here — is the ratio that surfaces the problem in lesson VII-05
before it becomes bloat.
What to take from this
- A dozen views answer real questions. Learn those, not all of them.
pg_stat_bgwriterhas three columns. Checkpoint metrics moved topg_stat_checkpointerin 17.- Alert on
deadlocks,temp_files,conflicts, andchecksum_failures— the last at any non-zero value. - Cache hit ratio is a trend. A page-cache hit counts as a miss.
sessions_abandoned/_fatal/_killedclimbing is a real signal.- Counters are cumulative. Graph rates, and watch
stats_reset. - A crash discards statistics; the gap looks like quiet.
Cross-course references
- Observability for Production Sysadmins — Part IX (Exporters) and Part LIX (Database observability) cover turning these views into series, and Part III (Metrics fundamentals) covers why a cumulative counter must be rated rather than graphed raw.
- Linux for Production Sysadmins — Part XLIV (Central monitoring) covers where the resulting series are stored and who can query them.
Quiz
Knowledge check · 6 questions
Q1. After upgrading to PostgreSQL 18, a checkpoint dashboard shows blank panels rather than errors. What is the likely cause?
Q2. A monitoring system reports a huge negative rate for deadlocks. What most likely happened?
Q3. Why is a 99.9% cache hit ratio not evidence that a cluster is healthy?
Q4. Which pg_stat_database columns warrant an alert when they increase? Select all that apply.
Q5. pg_stat_activity is a snapshot rather than a cumulative view, so a frequently-run 5 ms query is effectively invisible to it.
Q6. Why must cumulative statistics be graphed as rates, and what should a monitoring system do about resets?
Passing score: 75%. Answers are checked in this browser.